From 3b21cc55ff915aa91f1af56fe27bdca0ffd4bb05 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Thu, 8 Oct 2015 01:09:22 -0400 Subject: [PATCH] Lightweight CAs: enrol cert via profile subsystem Enrol new CA certs via the profile subsystem to ensure that the usual audit events are logged and to avoid the nasty ConfigStore hack used to generate the cert via CertUtil. This commit also fixes an issue where the new CA certificate does not have the correct Authority Key Identifier extension. Fixes: https://fedorahosted.org/pki/ticket/1624 Fixes: https://fedorahosted.org/pki/ticket/1632 --- .../src/com/netscape/ca/CertificateAuthority.java | 66 ++++++++++++++++++---- .../dogtagpki/server/ca/rest/AuthorityService.java | 5 ++ .../netscape/certsrv/ca/ICertificateAuthority.java | 3 + .../servlet/cert/CertEnrollmentRequestFactory.java | 15 +++-- .../cms/servlet/cert/EnrollmentProcessor.java | 15 ++++- 5 files changed, 88 insertions(+), 16 deletions(-) diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java index 449da301f71338402ae070a3247b8e7d2c78b05c..ea4515c353e4bf3d18acc54a3729b9d98fe064ed 100644 --- a/base/ca/src/com/netscape/ca/CertificateAuthority.java +++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java @@ -18,15 +18,18 @@ package com.netscape.ca; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.PrintStream; import java.math.BigInteger; import java.security.KeyPair; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; +import java.security.Signature; import java.security.cert.CRLException; import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; @@ -36,6 +39,7 @@ import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.TreeMap; import java.util.Vector; @@ -51,6 +55,7 @@ import netscape.ldap.LDAPException; import netscape.ldap.LDAPModification; import netscape.ldap.LDAPModificationSet; import netscape.ldap.LDAPSearchResults; +import netscape.security.pkcs.PKCS10; import netscape.security.util.DerOutputStream; import netscape.security.util.DerValue; import netscape.security.x509.AlgorithmId; @@ -59,6 +64,7 @@ import netscape.security.x509.CertificateIssuerName; import netscape.security.x509.CertificateSubjectName; import netscape.security.x509.CertificateVersion; import netscape.security.x509.X500Name; +import netscape.security.x509.X500Signer; import netscape.security.x509.X509CRLImpl; import netscape.security.x509.X509CertImpl; import netscape.security.x509.X509CertInfo; @@ -83,6 +89,7 @@ import org.mozilla.jss.pkix.cert.Extension; import org.mozilla.jss.pkix.primitive.Name; import com.netscape.certsrv.apps.CMS; +import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.authority.ICertAuthority; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.EPropertyNotFound; @@ -100,6 +107,7 @@ import com.netscape.certsrv.ca.ECAException; import com.netscape.certsrv.ca.ICRLIssuingPoint; import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.ca.IssuerUnavailableException; +import com.netscape.certsrv.cert.CertEnrollmentRequest; import com.netscape.certsrv.dbs.IDBSubsystem; import com.netscape.certsrv.dbs.certdb.ICertRecord; import com.netscape.certsrv.dbs.certdb.ICertificateRepository; @@ -110,19 +118,26 @@ import com.netscape.certsrv.ldap.ILdapConnFactory; import com.netscape.certsrv.logging.ILogger; import com.netscape.certsrv.ocsp.IOCSPService; import com.netscape.certsrv.policy.IPolicyProcessor; +import com.netscape.certsrv.profile.IEnrollProfile; +import com.netscape.certsrv.profile.IProfileSubsystem; +import com.netscape.certsrv.profile.IProfile; import com.netscape.certsrv.publish.ICRLPublisher; import com.netscape.certsrv.publish.IPublisherProcessor; import com.netscape.certsrv.request.ARequestNotifier; import com.netscape.certsrv.request.IPolicy; +import com.netscape.certsrv.request.IRequest; import com.netscape.certsrv.request.IRequestListener; import com.netscape.certsrv.request.IRequestNotifier; import com.netscape.certsrv.request.IRequestQueue; import com.netscape.certsrv.request.IRequestScheduler; import com.netscape.certsrv.request.IService; +import com.netscape.certsrv.request.RequestStatus; import com.netscape.certsrv.security.ISigningUnit; import com.netscape.certsrv.util.IStatsSubsystem; -import com.netscape.cms.servlet.csadmin.CertUtil; -import com.netscape.cmscore.base.PropConfigStore; +import com.netscape.cms.servlet.cert.EnrollmentProcessor; +import com.netscape.cms.servlet.cert.CertEnrollmentRequestFactory; +import com.netscape.cms.servlet.processors.CAProcessor; +import com.netscape.cmscore.base.ArgBlock; import com.netscape.cmscore.dbs.CRLRepository; import com.netscape.cmscore.dbs.CertRecord; import com.netscape.cmscore.dbs.CertificateRepository; @@ -2376,6 +2391,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori * @param description Optional string description of CA */ public ICertificateAuthority createCA( + IAuthToken authToken, String subjectDN, AuthorityID parentAID, String description) throws EBaseException { @@ -2385,7 +2401,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori "Parent CA \"" + parentAID + "\" does not exist"); ICertificateAuthority ca = parentCA.createSubCA( - subjectDN, description); + authToken, subjectDN, description); caMap.put(ca.getAuthorityID(), ca); return ca; } @@ -2406,6 +2422,7 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori * caller's responsibility. */ public ICertificateAuthority createSubCA( + IAuthToken authToken, String subjectDN, String description) throws EBaseException { @@ -2473,20 +2490,49 @@ public class CertificateAuthority implements ICertificateAuthority, ICertAuthori PublicKey pub = keypair.getPublic(); X509Key x509key = CryptoUtil.convertPublicKeyToX509Key(pub); + // Create pkcs10 request + CMS.debug("createSubCA: creating pkcs10 request"); + PKCS10 pkcs10 = new PKCS10(x509key); + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(keypair.getPrivate()); + pkcs10.encodeAndSign( + new X500Signer(signature, subjectX500Name)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + pkcs10.print(new PrintStream(out)); + String pkcs10String = out.toString(); + // Sign certificate - String algName = mSigningUnit.getDefaultAlgorithm(); - IConfigStore cs = new PropConfigStore("cs"); - cs.put(".profile", "caCert.profile"); - cs.put(".dn", subjectDN); - cs.put(".keyalgorithm", algName); - X509CertImpl cert = - CertUtil.createLocalCertWithCA(cs, x509key, "", "", "local", this); + Locale locale = Locale.getDefault(); + String profileId = "caCACert"; + IProfileSubsystem ps = (IProfileSubsystem) + CMS.getSubsystem(IProfileSubsystem.ID); + IProfile profile = ps.getProfile(profileId); + ArgBlock argBlock = new ArgBlock(); + argBlock.set("cert_request_type", "pkcs10"); + argBlock.set("cert_request", pkcs10String); + CertEnrollmentRequest certRequest = + CertEnrollmentRequestFactory.create(argBlock, profile, locale); + EnrollmentProcessor processor = + new EnrollmentProcessor("createSubCA", locale); + Map resultMap = processor.processEnrollment( + certRequest, null, authorityID, null, authToken); + IRequest requests[] = (IRequest[]) resultMap.get(CAProcessor.ARG_REQUESTS); + IRequest request = requests[0]; + Integer result = request.getExtDataInInteger(IRequest.RESULT); + if (result != null && !result.equals(IRequest.RES_SUCCESS)) + throw new EBaseException("createSubCA: certificate request submission resulted in error: " + result); + RequestStatus requestStatus = request.getRequestStatus(); + if (requestStatus != RequestStatus.COMPLETE) + throw new EBaseException("createSubCA: certificate request did not complete; status: " + requestStatus); // Add certificate to nssdb + X509CertImpl cert = request.getExtDataInCert(IEnrollProfile.REQUEST_ISSUED_CERT); cryptoManager.importCertPackage(cert.getEncoded(), nickname); } catch (Exception e) { // something went wrong; delete just-added entry conn.delete(dn); + CMS.debug("Error creating lightweight CA certificate"); + CMS.debug(e); throw new ECAException("Error creating lightweight CA certificate: " + e); } } catch (LDAPException e) { diff --git a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java index b23a4b853252dd4e5dadef3b3ef508113e1d5337..3504b005b480b597fb0a2246a43bd848e7d6afa0 100644 --- a/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java +++ b/base/ca/src/org/dogtagpki/server/ca/rest/AuthorityService.java @@ -48,6 +48,7 @@ import com.netscape.certsrv.ca.CANotLeafException; import com.netscape.certsrv.ca.CATypeException; import com.netscape.certsrv.ca.ICertificateAuthority; import com.netscape.certsrv.ca.IssuerUnavailableException; +import com.netscape.cms.realm.PKIPrincipal; import com.netscape.cms.servlet.base.PKIService; import com.netscape.cmsutil.util.Utils; @@ -178,8 +179,12 @@ public class AuthorityService extends PKIService implements AuthorityResource { throw new BadRequestException("Bad Authority ID: " + parentAIDString); } + PKIPrincipal principal = + (PKIPrincipal) servletRequest.getUserPrincipal(); + try { ICertificateAuthority subCA = hostCA.createCA( + principal.getAuthToken(), data.getDN(), parentAID, data.getDescription()); return createOKResponse(readAuthorityData(subCA)); } catch (IllegalArgumentException e) { diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java index 96bc392294f27d57d9795c2b1b793b8cbc001fda..376654e98b0276a2c2add8b98fb30bf339165b87 100644 --- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java +++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java @@ -34,6 +34,7 @@ import netscape.security.x509.X509CertInfo; import org.mozilla.jss.crypto.SignatureAlgorithm; +import com.netscape.certsrv.authentication.IAuthToken; import com.netscape.certsrv.base.EBaseException; import com.netscape.certsrv.base.IConfigStore; import com.netscape.certsrv.base.ISubsystem; @@ -562,6 +563,7 @@ public interface ICertificateAuthority extends ISubsystem { * Create a new sub-CA under the specified parent CA. */ public ICertificateAuthority createCA( + IAuthToken authToken, String dn, AuthorityID parentAID, String desc) throws EBaseException; @@ -572,6 +574,7 @@ public interface ICertificateAuthority extends ISubsystem { * caller's responsibility. */ public ICertificateAuthority createSubCA( + IAuthToken authToken, String dn, String desc) throws EBaseException; diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/CertEnrollmentRequestFactory.java b/base/server/cms/src/com/netscape/cms/servlet/cert/CertEnrollmentRequestFactory.java index d74a285f391ecf4fdbafe219d02f20e86ccf1848..2b608259fbe4994e4e3eb42604756b6814027f9b 100644 --- a/base/server/cms/src/com/netscape/cms/servlet/cert/CertEnrollmentRequestFactory.java +++ b/base/server/cms/src/com/netscape/cms/servlet/cert/CertEnrollmentRequestFactory.java @@ -37,6 +37,17 @@ public class CertEnrollmentRequestFactory { throws EProfileException { IArgBlock params = cmsReq.getHttpParams(); + CertEnrollmentRequest request = create(params, profile, locale); + + HttpServletRequest httpRequest = cmsReq.getHttpReq(); + request.setRemoteHost(httpRequest.getRemoteHost()); + request.setRemoteAddr(httpRequest.getRemoteAddr()); + + return request; + } + + public static CertEnrollmentRequest create(IArgBlock params, IProfile profile, Locale locale) + throws EProfileException { CertEnrollmentRequest request = new CertEnrollmentRequest(); request.setProfileId(profile.getId()); @@ -48,10 +59,6 @@ public class CertEnrollmentRequestFactory { request.addInput(addInput); } - HttpServletRequest httpRequest = cmsReq.getHttpReq(); - request.setRemoteHost(httpRequest.getRemoteHost()); - request.setRemoteAddr(httpRequest.getRemoteAddr()); - return request; } 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 dadd34cfe8b74ebbefa1af2d2141d5baee04755e..3e92d5948a236fd153414d5faa556b2f805a99b5 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 @@ -83,6 +83,15 @@ public class EnrollmentProcessor extends CertProcessor { } + public HashMap processEnrollment( + CertEnrollmentRequest data, + HttpServletRequest request, + AuthorityID aid, + AuthCredentials credentials) + throws EBaseException { + return processEnrollment(data, request, aid, credentials, null); + } + /** * Process the HTTP request *

@@ -104,7 +113,8 @@ public class EnrollmentProcessor extends CertProcessor { CertEnrollmentRequest data, HttpServletRequest request, AuthorityID aid, - AuthCredentials credentials) + AuthCredentials credentials, + IAuthToken authToken) throws EBaseException { try { @@ -153,7 +163,8 @@ public class EnrollmentProcessor extends CertProcessor { CMS.debug("EnrollmentProcessor: set sslClientCertProvider"); // before creating the request, authenticate the request - IAuthToken authToken = authenticate(request, null, authenticator, context, false, credentials); + if (authToken == null) + authToken = authenticate(request, null, authenticator, context, false, credentials); // authentication success, now authorize authorize(profileId, profile, authToken); -- 2.4.3