diff options
author | Fraser Tweedale <ftweedal@redhat.com> | 2015-01-28 02:41:10 -0500 |
---|---|---|
committer | Fraser Tweedale <ftweedal@redhat.com> | 2015-09-26 14:11:51 +1000 |
commit | 2a9f56d02b4a284cda6f8b61b250e1494f19a83e (patch) | |
tree | 9b12125932ed41a5dbe06f8dafb66656e78c7ad8 /base/server | |
parent | a5a50e95a691587e22335018538b4f578dfee6d1 (diff) | |
download | pki-2a9f56d02b4a284cda6f8b61b250e1494f19a83e.tar.gz pki-2a9f56d02b4a284cda6f8b61b250e1494f19a83e.tar.xz pki-2a9f56d02b4a284cda6f8b61b250e1494f19a83e.zip |
Lightweight CAs: initial support
This commit adds initial support for "lightweight CAs" - CAs that
inhabit an existing CA instance and share the request queue and
certificate database of the "top-level CA".
We initially support only sub-CAs under the top-level CA - either
direct sub-CAs or nested. The general design will support hosting
unrelated CAs but creation or import of unrelated CAs is not yet
implemented.
Part of: https://fedorahosted.org/pki/ticket/1213
Diffstat (limited to 'base/server')
11 files changed, 135 insertions, 30 deletions
diff --git a/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java index d0bfdb8a6..53edca3a9 100644 --- a/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java +++ b/base/server/cms/src/com/netscape/cms/profile/common/CAEnrollProfile.java @@ -29,6 +29,7 @@ import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.authority.IAuthority; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.SessionContext; +import com.netscape.certsrv.ca.AuthorityID; import com.netscape.certsrv.ca.ICAService; import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.connector.IConnector; @@ -95,8 +96,8 @@ public class CAEnrollProfile extends EnrollProfile { CMS.debug("CAEnrollProfile: execute reqId=" + request.getRequestId().toString()); ICertificateAuthority ca = (ICertificateAuthority) getAuthority(); - ICAService caService = (ICAService) ca.getCAService(); + ICAService caService = (ICAService) ca.getCAService(); if (caService == null) { throw new EProfileException("No CA Service"); } @@ -190,9 +191,13 @@ public class CAEnrollProfile extends EnrollProfile { if (setId != null) { sc.put("profileSetId", setId); } + AuthorityID aid = null; + String aidString = request.getExtDataInString(REQUEST_AUTHORITY_ID); + if (aidString != null) + aid = new AuthorityID(aidString); try { - theCert = caService.issueX509Cert(info, getId() /* profileId */, - id /* requestId */); + theCert = caService.issueX509Cert( + aid, info, getId() /* profileId */, id /* requestId */); } catch (EBaseException e) { CMS.debug(e.toString()); diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java index fe3b424a4..523e0117a 100644 --- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java +++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java @@ -190,6 +190,9 @@ public abstract class EnrollProfile extends BasicProfile if (locale != null) { result[i].setExtData(REQUEST_LOCALE, locale.getLanguage()); } + + // set requested CA + result[i].setExtData(REQUEST_AUTHORITY_ID, ctx.get(REQUEST_AUTHORITY_ID)); } return result; } diff --git a/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java index 095f8bb5f..bd71a4ef8 100644 --- a/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java +++ b/base/server/cms/src/com/netscape/cms/profile/def/AuthorityKeyIdentifierExtDefault.java @@ -20,20 +20,23 @@ package com.netscape.cms.profile.def; import java.io.IOException; import java.util.Locale; -import netscape.security.x509.AuthorityKeyIdentifierExtension; -import netscape.security.x509.KeyIdentifier; -import netscape.security.x509.PKIXExtensions; -import netscape.security.x509.X509CertInfo; - import com.netscape.certsrv.apps.CMS; import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.ca.AuthorityID; +import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.profile.EProfileException; +import com.netscape.certsrv.profile.IEnrollProfile; import com.netscape.certsrv.profile.IProfile; import com.netscape.certsrv.property.Descriptor; import com.netscape.certsrv.property.EPropertyException; import com.netscape.certsrv.property.IDescriptor; import com.netscape.certsrv.request.IRequest; +import netscape.security.x509.AuthorityKeyIdentifierExtension; +import netscape.security.x509.KeyIdentifier; +import netscape.security.x509.PKIXExtensions; +import netscape.security.x509.X509CertInfo; + /** * This class implements an enrollment default policy * that populates Authority Key Identifier extension @@ -161,18 +164,27 @@ public class AuthorityKeyIdentifierExtDefault extends CAEnrollDefault { */ public void populate(IRequest request, X509CertInfo info) throws EProfileException { - AuthorityKeyIdentifierExtension ext = createExtension(info); - + ICertificateAuthority ca = (ICertificateAuthority) + CMS.getSubsystem(CMS.SUBSYSTEM_CA); + String aidString = request.getExtDataInString( + IEnrollProfile.REQUEST_AUTHORITY_ID); + if (aidString != null) + ca = ca.getCA(new AuthorityID(aidString)); + if (ca == null) + throw new EProfileException("Could not reach requested CA"); + + AuthorityKeyIdentifierExtension ext = createExtension(ca, info); addExtension(PKIXExtensions.AuthorityKey_Id.toString(), ext, info); } - public AuthorityKeyIdentifierExtension createExtension(X509CertInfo info) { + public AuthorityKeyIdentifierExtension createExtension( + ICertificateAuthority ca, X509CertInfo info) { KeyIdentifier kid = null; String localKey = getConfig("localKey"); if (localKey != null && localKey.equals("true")) { kid = getKeyIdentifier(info); } else { - kid = getCAKeyIdentifier(); + kid = getCAKeyIdentifier(ca); } if (kid == null) diff --git a/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java index 1d1d05ed5..696830ead 100644 --- a/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java +++ b/base/server/cms/src/com/netscape/cms/profile/def/CAEnrollDefault.java @@ -68,9 +68,7 @@ public abstract class CAEnrollDefault extends EnrollDefault { return null; } - public KeyIdentifier getCAKeyIdentifier() { - ICertificateAuthority ca = (ICertificateAuthority) - CMS.getSubsystem(CMS.SUBSYSTEM_CA); + public KeyIdentifier getCAKeyIdentifier(ICertificateAuthority ca) { X509CertImpl caCert = ca.getCACert(); if (caCert == null) { // during configuration, we dont have the CA certificate diff --git a/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java b/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java index 4ebf075cb..fe77fd567 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java +++ b/base/server/cms/src/com/netscape/cms/servlet/base/PKIService.java @@ -56,7 +56,10 @@ public class PKIService { public static List<MediaType> MESSAGE_FORMATS = Arrays.asList( MediaType.APPLICATION_XML_TYPE, MediaType.APPLICATION_JSON_TYPE, - MediaType.APPLICATION_FORM_URLENCODED_TYPE + MediaType.APPLICATION_FORM_URLENCODED_TYPE, + MediaType.valueOf("application/pkix-cert"), + MediaType.valueOf("application/pkcs7-mime"), + MediaType.valueOf("application/x-pem-file") ); public final static int MIN_FILTER_LENGTH = 3; diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/CertRequestDAO.java b/base/server/cms/src/com/netscape/cms/servlet/cert/CertRequestDAO.java index c94ee1496..27d8b8262 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/cert/CertRequestDAO.java +++ b/base/server/cms/src/com/netscape/cms/servlet/cert/CertRequestDAO.java @@ -175,7 +175,7 @@ public class CertRequestDAO extends CMSRequestDAO { results = processor.processRenewal(data, request); } else { EnrollmentProcessor processor = new EnrollmentProcessor("caProfileSubmit", locale); - results = processor.processEnrollment(data, request); + results = processor.processEnrollment(data, request, null); } IRequest reqs[] = (IRequest[]) results.get(CAProcessor.ARG_REQUESTS); diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java index 960f997cd..e5b9a14df 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java +++ b/base/server/cms/src/com/netscape/cms/servlet/cert/EnrollmentProcessor.java @@ -30,6 +30,8 @@ import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.SessionContext; import com.netscape.certsrv.cert.CertEnrollmentRequest; +import com.netscape.certsrv.ca.AuthorityID; +import com.netscape.certsrv.profile.IEnrollProfile; import com.netscape.certsrv.profile.IProfile; import com.netscape.certsrv.profile.IProfileAuthenticator; import com.netscape.certsrv.profile.IProfileContext; @@ -98,7 +100,7 @@ public class EnrollmentProcessor extends CertProcessor { } CertEnrollmentRequest data = CertEnrollmentRequestFactory.create(cmsReq, profile, locale); - return processEnrollment(data, cmsReq.getHttpReq()); + return processEnrollment(data, cmsReq.getHttpReq(), null); } /** @@ -118,8 +120,11 @@ public class EnrollmentProcessor extends CertProcessor { * @param cmsReq the object holding the request and response information * @exception EBaseException an error has occurred */ - public HashMap<String, Object> processEnrollment(CertEnrollmentRequest data, HttpServletRequest request) - throws EBaseException { + public HashMap<String, Object> processEnrollment( + CertEnrollmentRequest data, + HttpServletRequest request, + AuthorityID aid) + throws EBaseException { try { if (CMS.debugOn()) { @@ -146,6 +151,10 @@ public class EnrollmentProcessor extends CertProcessor { } IProfileContext ctx = profile.createContext(); + + if (aid != null) + ctx.set(IEnrollProfile.REQUEST_AUTHORITY_ID, aid.toString()); + CMS.debug("EnrollmentProcessor: set Inputs into profile Context"); setInputsIntoContext(data, profile, ctx); diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java index 2826f477e..8558ec23f 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java +++ b/base/server/cms/src/com/netscape/cms/servlet/cert/RequestProcessor.java @@ -36,6 +36,10 @@ import com.netscape.certsrv.base.BadRequestException; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; import com.netscape.certsrv.base.IConfigStore; +import com.netscape.certsrv.ca.AuthorityID; +import com.netscape.certsrv.ca.CADisabledException; +import com.netscape.certsrv.ca.CANotFoundException; +import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.cert.CertReviewResponse; import com.netscape.certsrv.logging.ILogger; import com.netscape.certsrv.profile.EDeferException; @@ -327,6 +331,31 @@ public class RequestProcessor extends CertProcessor { } /** + * Ensure validity of AuthorityID and that CA exists and is enabled. + */ + private void ensureCAEnabled(String aidString) throws EBaseException { + AuthorityID aid = null; + try { + aid = new AuthorityID(aidString); + } catch (IllegalArgumentException e) { + // this shouldn't happen because request was already accepted + throw new BadRequestDataException("Invalid AuthorityID in request data"); + } + ICertificateAuthority ca = (ICertificateAuthority) + CMS.getSubsystem("ca"); + if (ca == null) + // this shouldn't happen + throw new CANotFoundException("Could not get host authority"); // shouldn't happen + ca = ca.getCA(aid); + if (ca == null) + // this shouldn't happen because request was already accepted + throw new CANotFoundException("Unknown CA: " + aidString); + if (!ca.getAuthorityEnabled()) + // authority was disabled after request was accepted + throw new CADisabledException("CA '" + aidString + "' is disabled"); + } + + /** * Approve request * <P> * @@ -346,11 +375,16 @@ public class RequestProcessor extends CertProcessor { * occurred */ private void approveRequest(IRequest req, CertReviewResponse data, IProfile profile, Locale locale) - throws EProfileException { + throws EBaseException { String auditMessage = null; String auditSubjectID = auditSubjectID(); String auditRequesterID = auditRequesterID(req); + // ensure target CA is enabled + String aidString = req.getExtDataInString(IEnrollProfile.REQUEST_AUTHORITY_ID); + if (aidString != null) + ensureCAEnabled(aidString); + try { profile.execute(req); req.setRequestStatus(RequestStatus.COMPLETE); diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java index 36b0e4d0d..c0729d881 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java +++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/CertUtil.java @@ -434,8 +434,19 @@ public class CertUtil { (signingKeyType.equals("dsa") && algorithm.contains("DSA"))); } + public static X509CertImpl createLocalCertWithCA(IConfigStore config, X509Key x509key, + String prefix, String certTag, String type, ICertificateAuthority ca) throws IOException { + return createLocalCert(config, x509key, prefix, certTag, type, ca, null); + } + public static X509CertImpl createLocalCert(IConfigStore config, X509Key x509key, String prefix, String certTag, String type, Context context) throws IOException { + return createLocalCert(config, x509key, prefix, certTag, type, null, context); + } + + public static X509CertImpl createLocalCert(IConfigStore config, X509Key x509key, + String prefix, String certTag, String type, + ICertificateAuthority ca, Context context) throws IOException { CMS.debug("Creating local certificate... certTag=" + certTag); String profile = null; @@ -446,13 +457,14 @@ public class CertUtil { } X509CertImpl cert = null; - ICertificateAuthority ca = null; ICertificateRepository cr = null; RequestId reqId = null; String profileId = null; IRequestQueue queue = null; IRequest req = null; + boolean caProvided = ca != null; + try { Boolean injectSAN = config.getBoolean( "service.injectSAN", false); @@ -468,7 +480,8 @@ public class CertUtil { } else { keyAlgorithm = config.getString(prefix + certTag + ".keyalgorithm"); } - ca = (ICertificateAuthority) CMS.getSubsystem( + if (!caProvided) + ca = (ICertificateAuthority) CMS.getSubsystem( ICertificateAuthority.ID); cr = ca.getCertificateRepository(); BigInteger serialNo = cr.getNextSerialNumber(); @@ -496,9 +509,9 @@ public class CertUtil { } CMS.debug("Cert Template: " + info.toString()); - String instanceRoot = config.getString("instanceRoot"); + String instanceRoot = CMS.getConfigStore().getString("instanceRoot"); - String configurationRoot = config.getString("configurationRoot"); + String configurationRoot = CMS.getConfigStore().getString("configurationRoot"); CertInfoProfile processor = new CertInfoProfile( instanceRoot + configurationRoot + profile); @@ -541,11 +554,18 @@ public class CertUtil { processor.populate(req, info); - String caPriKeyID = config.getString( - prefix + "signing" + ".privkey.id"); - byte[] keyIDb = CryptoUtil.string2byte(caPriKeyID); - PrivateKey caPrik = CryptoUtil.findPrivateKeyFromID( - keyIDb); + PrivateKey caPrik = null; + if (caProvided) { + java.security.PrivateKey pk = ca.getSigningUnit().getPrivateKey(); + if (!(pk instanceof PrivateKey)) + throw new IOException("CA Private key must be a JSS PrivateKey"); + caPrik = (PrivateKey) pk; + } else { + String caPriKeyID = config.getString( + prefix + "signing" + ".privkey.id"); + byte[] keyIDb = CryptoUtil.string2byte(caPriKeyID); + caPrik = CryptoUtil.findPrivateKeyFromID(keyIDb); + } if (caPrik == null) { CMS.debug("CertUtil::createSelfSignedCert() - " diff --git a/base/server/share/conf/schema-authority.ldif b/base/server/share/conf/schema-authority.ldif new file mode 100644 index 000000000..7d261f18f --- /dev/null +++ b/base/server/share/conf/schema-authority.ldif @@ -0,0 +1,8 @@ +dn: cn=schema +attributeTypes: ( authorityID-oid NAME 'authorityID' DESC 'Authority ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityKeyNickname-oid NAME 'authorityKeyNickname' DESC 'Authority key nickname' SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 X-ORIGIN 'user-defined' ) +attributeTypes: ( authorityParentID-oid NAME 'authorityParentID' DESC 'Authority Parent ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityEnabled-oid NAME 'authorityEnabled' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityDN-oid NAME 'authorityDN' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityParentDN-oid NAME 'authorityParentDN' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' ) +objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname $ authorityEnabled $ authorityDN ) MAY ( authorityParentID $ authorityParentDN $ description ) X-ORIGIN 'user defined' ) diff --git a/base/server/share/conf/schema.ldif b/base/server/share/conf/schema.ldif index 475758c5d..a15601ae7 100644 --- a/base/server/share/conf/schema.ldif +++ b/base/server/share/conf/schema.ldif @@ -667,3 +667,16 @@ dn: cn=schema changetype: modify add: objectClasses objectClasses: ( certProfile-oid NAME 'certProfile' DESC 'Certificate profile' SUP top STRUCTURAL MUST cn MAY ( classId $ certProfileConfig ) X-ORIGIN 'user defined' ) + +dn: cn=schema +changetype: modify +add: attributeTypes +attributeTypes: ( authorityID-oid NAME 'authorityID' DESC 'Authority ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityKeyNickname-oid NAME 'authorityKeyNickname' DESC 'Authority key nickname' SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 X-ORIGIN 'user-defined' ) +attributeTypes: ( authorityParentID-oid NAME 'authorityParentID' DESC 'Authority Parent ID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityEnabled-oid NAME 'authorityEnabled' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityDN-oid NAME 'authorityDN' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' ) +attributeTypes: ( authorityParentDN-oid NAME 'authorityParentDN' DESC 'Authority Enabled' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'user defined' ) +- +add: objectClasses +objectClasses: ( authority-oid NAME 'authority' DESC 'Certificate Authority' SUP top STRUCTURAL MUST ( cn $ authorityID $ authorityKeyNickname $ authorityEnabled $ authorityDN ) MAY ( authorityParentID $ authorityParentDN $ description ) X-ORIGIN 'user defined' ) |