From 35fc82430b8abcb661d112ba3814de95b377f070 Mon Sep 17 00:00:00 2001 From: Matthew Harmsen Date: Jun 21 2017 17:46:12 +0000 Subject: Resolves: dogtag Pagure Issues #2540,2617,2619,2643,2719,2722,2728,2721,2737,2741 - dogtagpki Pagure Issue #2540 - Creating symmetric key (sharedSecret) using tkstool is failing when operating system is in FIPS mode. (jmagne) - dogtagpki Pagure Issue #2617 - Allow CA to process pre-signed CMC non-signing certificate requests (cfu) - dogtagpki Pagure Issue #2619 - Allow CA to process pre-signed CMC revocation non-signing cert requests (cfu) - dogtagpki Pagure Issue #2643 - Session timeout for PKI console (edewata) - dogtagpki Pagure Issue #2719 - change the way aes clients refer to aes keysets (vakwetu) - dogtagpki Pagure Issue #2722 - dont reuse IVs in the CMC code (vakwetu) - dogtagpki Pagure Issue #2728 - In keywrap mode, key recovery on KRA with HSM causes KRA to crash (ftweedal) - dogtagpki Pagure Issue #2721 - Key recovery using externalReg fails with java null pointer exception on KRA (vakwetu) - dogtagpki Pagure Issue #2737 - CMC: check HTTPS client authentication cert against CMC signer (cfu) - dogtagpki Pagure Issue #2741 - Unable to find keys in the p12 file after deleting the any of the subsystem certs from it (ftweedal) --- diff --git a/.gitignore b/.gitignore index f6ec24f..98c9d81 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ /pki-core-10.4.4.tar.gz /pki-core-10.4.5.tar.gz /pki-core-10.4.7.tar.gz +/pki-core-10.4.8.tar.gz diff --git a/pki-core-CMC-check-HTTPS-client-authentication-cert.patch b/pki-core-CMC-check-HTTPS-client-authentication-cert.patch new file mode 100644 index 0000000..51e9a74 --- /dev/null +++ b/pki-core-CMC-check-HTTPS-client-authentication-cert.patch @@ -0,0 +1,601 @@ +commit 63c9582009b3858a6878863b9658d04c9aad45c1 +Author: Christina Fu +Date: Wed Jun 14 14:57:10 2017 -0700 + + Ticket#2737 CMC: check HTTPS client authentication cert against CMC signer + + This patch adds enforcement in CMCUserSignedAuth to make sure SSL client authentication is performed and the authenticated cert matches that of the CMC signing cert. + Some auditing adjustments are also done. + +diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg +index d1bf7db..4da7429 100644 +--- a/base/ca/shared/conf/CS.cfg ++++ b/base/ca/shared/conf/CS.cfg +@@ -734,11 +734,10 @@ ca.publish.rule.instance.LdapXCertRule.pluginName=Rule + ca.publish.rule.instance.LdapXCertRule.predicate= + ca.publish.rule.instance.LdapXCertRule.publisher=LdapCrossCertPairPublisher + ca.publish.rule.instance.LdapXCertRule.type=xcert +-cmc.cert.confirmRequired=false + cmc.popLinkWitnessRequired=false +-cmc.revokeCert.verify=true + cmc.revokeCert.sharedSecret.class=com.netscape.cms.authentication.SharedSecret + cmc.sharedSecret.class=com.netscape.cms.authentication.SharedSecret ++cmc.token=internal + cms.passwordlist=internaldb,replicationdb + cms.password.ignore.publishing.failure=true + cms.version=@APPLICATION_VERSION_MAJOR@.@APPLICATION_VERSION_MINOR@ +diff --git a/base/common/src/com/netscape/certsrv/base/SessionContext.java b/base/common/src/com/netscape/certsrv/base/SessionContext.java +index 8bcb3c1..9323e6e 100644 +--- a/base/common/src/com/netscape/certsrv/base/SessionContext.java ++++ b/base/common/src/com/netscape/certsrv/base/SessionContext.java +@@ -56,6 +56,13 @@ public class SessionContext extends Hashtable { + * Principal name object of the signed CMC request + */ + public static final String CMC_SIGNER_PRINCIPAL = "cmcSignerPrincipal"; ++ public static final String CMC_SIGNER_INFO = "cmcSignerInfo"; ++ public static final String CMC_REQUEST_CERT_SUBJECT = "cmcRequestCertSubject"; ++ ++ /** ++ * authenticated SSL client certificate ++ */ ++ public static final String SSL_CLIENT_CERT = "sslClientCert"; + + /** + * User object of the authenticated user in the current thread. +diff --git a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java +index 2e4d6dc..6c3ee8f 100644 +--- a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java ++++ b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java +@@ -28,6 +28,7 @@ package com.netscape.cms.authentication; + import java.io.ByteArrayInputStream; + import java.io.ByteArrayOutputStream; + import java.io.IOException; ++import java.security.cert.X509Certificate; + import java.math.BigInteger; + import java.security.MessageDigest; + import java.security.PublicKey; +@@ -260,11 +261,27 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + CMS.debug(method + "begins"); + + String auditMessage = null; +- String auditSubjectID = auditSubjectID(); ++ String auditSubjectID = getAuditSubjectID(); + String auditReqType = ILogger.UNIDENTIFIED; +- String auditCertSubject = ILogger.UNIDENTIFIED; ++ String requestCertSubject = ILogger.UNIDENTIFIED; + String auditSignerInfo = ILogger.UNIDENTIFIED; + ++ SessionContext auditContext = SessionContext.getExistingContext(); ++ ++ // create audit context if clientCert exists ++ X509Certificate clientCert = ++ (X509Certificate) auditContext.get(SessionContext.SSL_CLIENT_CERT); ++ // null is okay, as it is not required in case of self-sign; ++ // will be checked later ++ if (clientCert != null) { ++ try { ++ createAuditSubjectFromCert(auditContext, clientCert); ++ } catch (IOException e) { ++ //unlikely, and not necessarily required at this point ++ CMS.debug("CMSUserSignedAuth: authenticate: after createAuditSubjectFromCert call; " + e); ++ } ++ } ++ + // ensure that any low-level exceptions are reported + // to the signed audit log and stored as failures + try { +@@ -296,8 +313,6 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + throw new EInvalidCredentials(msg); + } + +- SessionContext auditContext = SessionContext.getExistingContext(); +- + // authenticate by checking CMC. + + // everything OK. +@@ -364,13 +379,13 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + } + // reset value of auditSignerInfo + if (uid != null && !uid.equals(ILogger.UNIDENTIFIED)) { +- CMS.debug(method + "setting auditSignerInfo to uid:" + uid.trim()); +- auditSignerInfo = uid.trim(); ++ //CMS.debug(method + "setting auditSignerInfo to uid:" + uid.trim()); ++ //auditSignerInfo = uid.trim(); + auditSubjectID = uid.trim(); + authToken.set(IAuthToken.USER_ID, auditSubjectID); + } else if (userid != null && !userid.equals(ILogger.UNIDENTIFIED)) { +- CMS.debug(method + "setting auditSignerInfo to userid:" + userid); +- auditSignerInfo = userid.trim(); ++ //CMS.debug(method + "setting auditSignerInfo to userid:" + userid); ++ //auditSignerInfo = userid.trim(); + auditSubjectID = userid.trim(); + authToken.set(IAuthToken.USER_ID, auditSubjectID); + } +@@ -538,16 +553,17 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + } + + PKCS10 pkcs10 = new PKCS10(ostream.toByteArray(), sigver); +- // reset value of auditCertSubject ++ // reset value of requestCertSubject + X500Name tempName = pkcs10.getSubjectName(); + CMS.debug(method + "request subject name=" + tempName.toString()); + if (tempName != null) { +- auditCertSubject = tempName.toString().trim(); +- if (auditCertSubject.equals("")) { +- auditCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE; ++ requestCertSubject = tempName.toString().trim(); ++ if (requestCertSubject.equals("")) { ++ requestCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + authToken.set(AuthToken.TOKEN_CERT_SUBJECT, +- auditCertSubject/*tempName.toString()*/); ++ requestCertSubject/*tempName.toString()*/); ++ auditContext.put(SessionContext.CMC_REQUEST_CERT_SUBJECT, requestCertSubject); + } + + if (selfSigned) { +@@ -632,17 +648,18 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + // xxx do we need to do anything else? + X509CertInfo certInfo = CMS.getDefaultX509CertInfo(); + +- // reset value of auditCertSubject ++ // reset value of requestCertSubject + if (name != null) { + String ss = name.getRFC1485(); + +- CMS.debug(method + "setting auditCertSubject to: " + ss); +- auditCertSubject = ss; +- if (auditCertSubject.equals("")) { +- auditCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE; ++ CMS.debug(method + "setting requestCertSubject to: " + ss); ++ requestCertSubject = ss; ++ if (requestCertSubject.equals("")) { ++ requestCertSubject = ILogger.SIGNED_AUDIT_EMPTY_VALUE; + } + + authToken.set(AuthToken.TOKEN_CERT_SUBJECT, ss); ++ auditContext.put(SessionContext.CMC_REQUEST_CERT_SUBJECT, requestCertSubject); + //authToken.set("uid", uid); + //authToken.set("userid", userid); + } +@@ -696,10 +713,15 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + + authToken.set("uid", uid); + authToken.set("userid", userid); ++ } catch (EMissingCredential e) { ++ throw e; ++ } catch (EInvalidCredentials e) { ++ throw e; + } catch (Exception e) { +- CMS.debug(method + e); ++ //CMS.debug(method + e); + //Debug.printStackTrace(e); +- throw new EInvalidCredentials(e.toString()); ++ //throw new EInvalidCredentials(e.toString()); ++ throw e; + } + + // For accuracy, make sure revocation by shared secret doesn't +@@ -709,11 +731,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS, +- auditSubjectID, ++ getAuditSubjectID(), + ILogger.SUCCESS, + auditReqType, +- auditCertSubject, +- auditSignerInfo); ++ getRequestCertSubject(auditContext), ++ getAuditSignerInfo(auditContext)); + + audit(auditMessage); + } else { +@@ -725,17 +747,6 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + return authToken; + } catch (EMissingCredential eAudit1) { + CMS.debug(method + eAudit1); +- // store a message in the signed audit log file +- auditMessage = CMS.getLogMessage( +- AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE, +- auditSubjectID, +- ILogger.FAILURE, +- auditReqType, +- auditCertSubject, +- auditSignerInfo, +- eAudit1.toString()); +- +- audit(auditMessage); + + // rethrow the specific exception to be handled later + throw eAudit1; +@@ -744,11 +755,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE, +- auditSubjectID, ++ getAuditSubjectID(), + ILogger.FAILURE, + auditReqType, +- auditCertSubject, +- auditSignerInfo, ++ getRequestCertSubject(auditContext), ++ getAuditSignerInfo(auditContext), + eAudit2.toString()); + + audit(auditMessage); +@@ -760,11 +771,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE, +- auditSubjectID, ++ getAuditSubjectID(), + ILogger.FAILURE, + auditReqType, +- auditCertSubject, +- auditSignerInfo, ++ getRequestCertSubject(auditContext), ++ getAuditSignerInfo(auditContext), + eAudit3.toString()); + + audit(auditMessage); +@@ -776,17 +787,17 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + // store a message in the signed audit log file + auditMessage = CMS.getLogMessage( + AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE, +- auditSubjectID, ++ getAuditSubjectID(), + ILogger.FAILURE, + auditReqType, +- auditCertSubject, +- auditSignerInfo, ++ getRequestCertSubject(auditContext), ++ getAuditSignerInfo(auditContext), + eAudit4.toString()); + + audit(auditMessage); + +- // rethrow the specific exception to be handled later +- throw eAudit4; ++ // rethrow the exception to be handled later ++ throw new EBaseException(eAudit4); + } + } + +@@ -935,8 +946,9 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + SessionContext auditContext, // to capture info in case of failure + AuthToken authToken, + SignedData cmcFullReq) +- throws EBaseException { ++ throws EBaseException, EInvalidCredentials, EMissingCredential { + String method = "CMCUserSignedAuth: verifySignerInfo: "; ++ String msg = ""; + CMS.debug(method + "begins"); + EncapsulatedContentInfo ci = cmcFullReq.getContentInfo(); + OBJECT_IDENTIFIER id = ci.getContentType(); +@@ -1001,7 +1013,7 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + if (cmcFullReq.hasCertificates()) { + SET certs = cmcFullReq.getCertificates(); + int numCerts = certs.size(); +- java.security.cert.X509Certificate[] x509Certs = new java.security.cert.X509Certificate[1]; ++ X509Certificate[] x509Certs = new X509Certificate[1]; + byte[] certByteArray = new byte[0]; + for (int j = 0; j < numCerts; j++) { + Certificate certJss = (Certificate) certs.elementAt(j); +@@ -1029,25 +1041,44 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + } + + CMS.debug(method + "start checking signature"); +- String CN = null; + if (cert == null) { + // find from certDB + CMS.debug(method + "verifying signature"); + si.verify(digest, id); + } else { +- CMS.debug(method + "found signing cert... verifying"); ++ CMS.debug(method + "found CMC signing cert... verifying"); ++ ++ X509Certificate clientCert = ++ (X509Certificate) auditContext.get(SessionContext.SSL_CLIENT_CERT); ++ // user-signed case requires ssl client authentication ++ if (clientCert == null) { ++ createAuditSubjectFromCert(auditContext, x509Certs[0]); ++ msg = "missing SSL client authentication certificate;"; ++ CMS.debug(method + msg); ++ s.close(); ++ throw new EMissingCredential( ++ CMS.getUserMessage("CMS_AUTHENTICATION_NO_CERT")); ++ } ++ netscape.security.x509.X500Name clientPrincipal = ++ (X500Name) clientCert.getSubjectDN(); + +- // capture auditSubjectID first in case of failure +- netscape.security.x509.X500Name principal = ++ netscape.security.x509.X500Name cmcPrincipal = + (X500Name) x509Certs[0].getSubjectDN(); + + // capture signer principal to be checked against + // cert subject principal later in CMCOutputTemplate + // in case of user signed revocation +- auditContext.put(SessionContext.CMC_SIGNER_PRINCIPAL, principal); +- CN = principal.getCommonName(); //tempToken.get("userid"); +- CMS.debug(method + " Principal name = " + CN); +- auditContext.put(SessionContext.USER_ID, CN); ++ auditContext.put(SessionContext.CMC_SIGNER_PRINCIPAL, cmcPrincipal); ++ auditContext.put(SessionContext.CMC_SIGNER_INFO, cmcPrincipal.getCommonName()); ++ ++ // check ssl client cert against cmc signer ++ if (!clientPrincipal.equals(cmcPrincipal)) { ++ msg = "SSL client authentication certificate and CMC signer do not match"; ++ CMS.debug(method + msg); ++ s.close(); ++ throw new EInvalidCredentials( ++ CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg); ++ } + + PublicKey signKey = cert.getPublicKey(); + PrivateKey.Type keyType = null; +@@ -1064,10 +1095,11 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + byte publicKeyData[] = ((X509Key) signKey).getEncoded(); + pubK = PK11ECPublicKey.fromSPKI(/*keyType,*/ publicKeyData); + } else { +- CMS.debug(method + "unsupported signature algorithm: " + alg); ++ msg = "unsupported signature algorithm: " + alg; ++ CMS.debug(method + msg); + s.close(); + throw new EInvalidCredentials( +- CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); ++ CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg); + } + + String tokenName = CMS.getConfigStore().getString("ca.requestVerify.token", +@@ -1095,9 +1127,10 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + // ...or not; I think it just checks usage and + // validity, but not revocation status + if (!cm.isCertValid(certByteArray, true, CryptoManager.CertUsage.SSLClient)) { +- CMS.debug(method + "CMC signature failed to be verified"); ++ msg = "CMC signing cert is invalid"; ++ CMS.debug(method + msg); + s.close(); +- throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); ++ throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg); + } else { + CMS.debug(method + "CMC signature verified; but signer not yet;"); + } +@@ -1105,28 +1138,28 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + + // now check revocation status of the cert + if (CMS.isRevoked(x509Certs)) { +- CMS.debug(method + "CMC signing cert is a revoked certificate"); ++ msg = "CMC signing cert is a revoked certificate"; ++ CMS.debug(method + msg); + s.close(); +- throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); ++ throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg); + } + try { //do this again anyways + cert.checkValidity(); + } catch (CertificateExpiredException e) { +- CMS.debug(method + "CMC signing cert is an expired certificate"); ++ msg = "CMC signing cert is an expired certificate"; ++ CMS.debug(method + msg); + s.close(); +- throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); ++ throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg); + } catch (Exception e) { + CMS.debug(method + e.toString()); + s.close(); +- throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); ++ throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + e.toString()); + } + + IAuthToken tempToken = new AuthToken(null); +-/* + netscape.security.x509.X500Name tempPrincipal = (X500Name) x509Certs[0].getSubjectDN(); + String CN = tempPrincipal.getCommonName(); //tempToken.get("userid"); + CMS.debug(method + " Principal name = " + CN); +-*/ + + BigInteger certSerial = x509Certs[0].getSerialNumber(); + CMS.debug(method + " verified cert serial=" + certSerial.toString()); +@@ -1137,7 +1170,9 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + return tempToken; + + } else { +- CMS.debug(method + "no certificate found in cmcFullReq"); ++ msg = "no certificate found in cmcFullReq"; ++ CMS.debug(method + msg); ++ throw new EMissingCredential(msg); + } + } else if (sid.getType().equals(SignerIdentifier.SUBJECT_KEY_IDENTIFIER)) { + CMS.debug(method + "SignerIdentifier type: SUBJECT_KEY_IDENTIFIER"); +@@ -1150,19 +1185,20 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + s.close(); + return tempToken; + } else { +- CMS.debug(method + "unsupported SignerIdentifier type"); ++ msg = "unsupported SignerIdentifier type"; ++ CMS.debug(method + msg); ++ throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL") + ":" + msg); + } + } //for + ++ } catch (EMissingCredential e) { ++ throw e; ++ } catch (EInvalidCredentials e) { ++ throw e; + } catch (InvalidBERException e) { +- CMS.debug(method + e.toString()); +- } catch (IOException e) { +- CMS.debug(method + e.toString()); +- } catch (NotInitializedException e) { +- CMS.debug(method + e.toString()); ++ CMS.debug(method + e); + } catch (Exception e) { +- CMS.debug(method + e.toString()); +- throw new EInvalidCredentials(CMS.getUserMessage("CMS_AUTHENTICATION_INVALID_CREDENTIAL")); ++ CMS.debug(method + e); + } finally { + if ((tokenSwitched == true) && (savedToken != null)) { + cm.setThreadToken(savedToken); +@@ -1173,6 +1209,21 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + + } + ++ private void createAuditSubjectFromCert ( ++ SessionContext auditContext, ++ X509Certificate cert) ++ throws IOException { ++ String method = "CMCUserSignedAuth:createAuditSubjectFromCert: "; ++ ++ // capture auditSubjectID first in case of failure ++ netscape.security.x509.X500Name principal = ++ (X500Name) cert.getSubjectDN(); ++ ++ String CN = principal.getCommonName(); ++ CMS.debug(method + " Principal name = " + CN); ++ auditContext.put(SessionContext.USER_ID, CN); ++ } ++ + public String[] getExtendedPluginInfo(Locale locale) { + return null; + } +@@ -1274,7 +1325,7 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + * + * @return id string containing the signed audit log message SubjectID + */ +- private String auditSubjectID() { ++ private String getAuditSubjectID() { + // if no signed audit object exists, bail + if (mSignedAuditLogger == null) { + return null; +@@ -1299,4 +1350,21 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo, + + return subjectID; + } ++ ++ private String getAuditSignerInfo(SessionContext auditContext) { ++ String signerSubject = (String)auditContext.get(SessionContext.CMC_SIGNER_INFO); ++ if (signerSubject == null) ++ signerSubject = "$Unidentified$"; ++ ++ return signerSubject; ++ } ++ ++ private String getRequestCertSubject(SessionContext auditContext) { ++ String certSubject = (String)auditContext.get(SessionContext.CMC_REQUEST_CERT_SUBJECT); ++ if (certSubject == null) ++ certSubject = "$Unidentified$"; ++ ++ return certSubject; ++ } ++ + } +diff --git a/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java b/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java +index 33cc7a9..030995a 100644 +--- a/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java ++++ b/base/server/cms/src/com/netscape/cms/profile/constraint/UniqueKeyConstraint.java +@@ -219,12 +219,14 @@ public class UniqueKeyConstraint extends EnrollConstraint { + Date origNotAfter = null; + boolean first = true; + while (e != null && e.hasMoreElements()) { ++ CMS.debug(method + msg); + ICertRecord rec = e.nextElement(); + BigInteger serial = rec.getSerialNumber(); ++ msg = msg + "existing cert with same key found: " + serial.toString() + ";"; + + if (rec.getStatus().equals(ICertRecord.STATUS_REVOKED) + || rec.getStatus().equals(ICertRecord.STATUS_REVOKED_EXPIRED)) { +- msg = msg + "revoked cert cannot be renewed: serial=" + serial.toString() + ";"; ++ msg = msg + "revoked cert cannot be renewed;"; + CMS.debug(method + msg); + rejected = true; + // this has to break +@@ -232,7 +234,7 @@ public class UniqueKeyConstraint extends EnrollConstraint { + } + if (!rec.getStatus().equals(ICertRecord.STATUS_VALID) + && !rec.getStatus().equals(ICertRecord.STATUS_EXPIRED)) { +- CMS.debug(method + "invalid cert cannot be renewed; continue:" + serial.toString()); ++ CMS.debug(method + "invalid cert cannot be renewed; continue;" + serial.toString()); + // can still find another one to renew + continue; + } +@@ -297,7 +299,7 @@ public class UniqueKeyConstraint extends EnrollConstraint { + } // (size > 0) + + if (rejected == true) { +- CMS.debug(method + " rejected"); ++ CMS.debug(method + " rejected: " + msg); + throw new ERejectException(msg); + } else { + CMS.debug(method + " approved"); +diff --git a/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java b/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java +index 9dc7470..65dc06a 100644 +--- a/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java ++++ b/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java +@@ -843,6 +843,10 @@ public abstract class CMSServlet extends HttpServlet { + * get ssl client authenticated certificate + */ + protected X509Certificate getSSLClientCertificate(HttpServletRequest httpReq) throws EBaseException { ++ return getSSLClientCertificate(httpReq, true); ++ } ++ ++ protected X509Certificate getSSLClientCertificate(HttpServletRequest httpReq, boolean clientCertRequired) throws EBaseException { + + X509Certificate cert = null; + +@@ -855,7 +859,11 @@ public abstract class CMSServlet extends HttpServlet { + X509Certificate[] allCerts = (X509Certificate[]) httpReq.getAttribute(CERT_ATTR); + + if (allCerts == null || allCerts.length == 0) { +- throw new EBaseException("You did not provide a valid certificate for this operation"); ++ if (!clientCertRequired) { ++ return null; ++ } else { ++ throw new EBaseException("You did not provide a valid certificate for this operation"); ++ } + } + + cert = allCerts[0]; +diff --git a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java +index 330b5ff..73195e9 100644 +--- a/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java ++++ b/base/server/cms/src/com/netscape/cms/servlet/profile/ProfileSubmitCMCServlet.java +@@ -19,6 +19,7 @@ package com.netscape.cms.servlet.profile; + + import java.io.InputStream; + import java.io.OutputStream; ++import java.security.cert.X509Certificate; + import java.util.Enumeration; + import java.util.Locale; + +@@ -169,6 +170,12 @@ public class ProfileSubmitCMCServlet extends ProfileServlet { + String authMgrID = authenticator.getName(); + SessionContext sc = SessionContext.getContext(); + ++ X509Certificate clientCert = ++ getSSLClientCertificate(request, false /*cert may not be required*/); ++ if (clientCert != null) { ++ sc.put(SessionContext.SSL_CLIENT_CERT, clientCert); ++ } ++ + try { + authToken = authenticator.authenticate(credentials); + if (sc != null) { +diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties +index 9490098..5e51440 100644 +--- a/base/server/cmsbundle/src/LogMessages.properties ++++ b/base/server/cmsbundle/src/LogMessages.properties +@@ -2208,10 +2208,10 @@ LOGGING_SIGNED_AUDIT_CMC_SIGNED_REQUEST_SIG_VERIFY_5=:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}] User signed CMC request signature verification success +-LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE_6=:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}][info={5}] User signed CMC request signature verification failure ++LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE_6=:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][CMCSignerInfo={4}][info={5}] User signed CMC request signature verification failure + + # LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST + # - used for TPS to TKS to get random challenge data diff --git a/pki-core-Fix-3DES-archival.patch b/pki-core-Fix-3DES-archival.patch new file mode 100644 index 0000000..49bee6f --- /dev/null +++ b/pki-core-Fix-3DES-archival.patch @@ -0,0 +1,66 @@ +commit 89f14cc5b7858e60107dc0776a59394bdfb8edaf +Author: Ade Lee +Date: Fri Jun 16 14:48:27 2017 -0400 + + Fix 3DES archival + + A previous commit mistakenly conflated the wrapping parameters for + DES and DES3 cases, resulting in incorrect data being stored if the + storage was successful at all. This broke ipa vault and probably + also token key archival and recovery. + + This patch sets the right parameters for the 3DES case again. + Part of BZ# 1458043 + + Change-Id: Iae884715a0f510a4d492d64fac3d82cb8100deb4 + +diff --git a/base/util/src/netscape/security/util/WrappingParams.java b/base/util/src/netscape/security/util/WrappingParams.java +index cda8870..ded572f 100644 +--- a/base/util/src/netscape/security/util/WrappingParams.java ++++ b/base/util/src/netscape/security/util/WrappingParams.java +@@ -67,6 +67,10 @@ public class WrappingParams { + // New clients set this correctly. + // We'll assume the old DES3 wrapping here. + encrypt = EncryptionAlgorithm.DES_CBC_PAD; ++ } else if (encryptOID.equals(CryptoUtil.KW_DES_CBC_PAD.toString())) { ++ encrypt = EncryptionAlgorithm.DES3_CBC_PAD; ++ } else if (encryptOID.equals(CryptoUtil.KW_AES_CBC_PAD.toString())) { ++ encrypt = EncryptionAlgorithm.AES_128_CBC_PAD; + } else { + encrypt = EncryptionAlgorithm.fromOID(new OBJECT_IDENTIFIER(encryptOID)); + } +@@ -135,23 +139,26 @@ public class WrappingParams { + payloadWrapAlgorithm = KeyWrapAlgorithm.AES_KEY_WRAP_PAD; + payloadEncryptionAlgorithm = EncryptionAlgorithm.AES_128_CBC_PAD; + skLength = 128; +- } +- +- if (kwAlg == KeyWrapAlgorithm.AES_CBC_PAD) { ++ } else if (kwAlg == KeyWrapAlgorithm.AES_CBC_PAD) { + skType = SymmetricKey.AES; + skKeyGenAlgorithm = KeyGenAlgorithm.AES; + payloadWrapAlgorithm = KeyWrapAlgorithm.AES_CBC_PAD; + payloadEncryptionAlgorithm = EncryptionAlgorithm.AES_128_CBC_PAD; + skLength = 128; +- } +- +- if (kwAlg == KeyWrapAlgorithm.DES3_CBC_PAD || kwAlg == KeyWrapAlgorithm.DES_CBC_PAD) { ++ } else if (kwAlg == KeyWrapAlgorithm.DES3_CBC_PAD) { ++ skType = SymmetricKey.DES3; ++ skKeyGenAlgorithm = KeyGenAlgorithm.DES3; ++ skWrapAlgorithm = KeyWrapAlgorithm.DES3_CBC_PAD; ++ payloadWrapAlgorithm = KeyWrapAlgorithm.DES3_CBC_PAD; ++ payloadEncryptionAlgorithm = EncryptionAlgorithm.DES3_CBC_PAD; ++ skLength = payloadEncryptionAlgorithm.getKeyStrength(); ++ } else if (kwAlg == KeyWrapAlgorithm.DES_CBC_PAD) { + skType = SymmetricKey.DES; + skKeyGenAlgorithm = KeyGenAlgorithm.DES; + skWrapAlgorithm = KeyWrapAlgorithm.DES3_CBC_PAD; + payloadWrapAlgorithm = KeyWrapAlgorithm.DES3_CBC_PAD; +- payloadEncryptionAlgorithm = EncryptionAlgorithm.DES3_CBC_PAD; +- skLength = 0; ++ payloadEncryptionAlgorithm = EncryptionAlgorithm.DES_CBC_PAD; ++ skLength = payloadEncryptionAlgorithm.getKeyStrength(); + } + + if (priKeyAlgo.equals("EC")) { diff --git a/pki-core-Fix-regression-in-pkcs12-key-bag-creation.patch b/pki-core-Fix-regression-in-pkcs12-key-bag-creation.patch new file mode 100644 index 0000000..87e426e --- /dev/null +++ b/pki-core-Fix-regression-in-pkcs12-key-bag-creation.patch @@ -0,0 +1,96 @@ +commit a411492fe5ad2030bb9f18db9a8ed8d1c45ee7de +Author: Fraser Tweedale +Date: Thu Jun 15 12:38:26 2017 +1000 + + Fix regression in pkcs12 key bag creation + + Commit 633c7c6519c925af7e3700adff29961d72435c7f changed the PKCS #12 + file handing to never deal with raw private key material. + PKCS12Util.addKeyBag() was changed to export the PrivateKey handle, + or fail. This change missed this case where a PKCS #12 file is + loaded from file, possibly modified, then written back to a file, + without involving an NSSDB. One example is pkcs12-cert-del which + deletes a certificate and associated key from a PKCS #12 file. + + Fix the PKCS12Util.addKeyBag() method to use the stored + EncryptedPricateKeyInfo if available, otherwise export the + PrivateKey handle. + + Fixes: https://pagure.io/dogtagpki/issue/2741 + Change-Id: Ib8098126bc5a79b5dae19103e25b270e2f10ab5a + +diff --git a/base/util/src/netscape/security/pkcs/PKCS12Util.java b/base/util/src/netscape/security/pkcs/PKCS12Util.java +index 31c7126..1bc1bae 100644 +--- a/base/util/src/netscape/security/pkcs/PKCS12Util.java ++++ b/base/util/src/netscape/security/pkcs/PKCS12Util.java +@@ -102,33 +102,49 @@ public class PKCS12Util { + icert.setObjectSigningTrust(PKCS12.decodeFlags(flags[2])); + } + +- /** +- * Used during EXPORT to add a private key to the PKCS12. ++ /** Add a private key to the PKCS #12 object. ++ * ++ * The PKCS12KeyInfo object received comes about in two ++ * different scenarios: ++ * ++ * - The private key could be in encrypted byte[] form (e.g. ++ * when we have merely loaded a PKCS #12 file for inspection ++ * or e.g. to delete a certificate and its associated key). ++ * In this case we simply re-use this encrypted private key ++ * info byte[]. + * +- * The private key is exported directly from the token, into +- * an EncryptedPrivateKeyInfo value, then added as a +- * "Shrouded Key Bag" to the PKCS #12 object. Unencrypted +- * key material is never seen. ++ * - The private key could be a be an NSS PrivateKey handle. In ++ * this case we must export the PrivateKey from the token to ++ * obtain the EncryptedPrivateKeyInfo. ++ * ++ * The common final step is to add the encrypted private key ++ * data to a "Shrouded Key Bag" to the PKCS #12 object. ++ * Unencrypted key material is never seen. + */ + public void addKeyBag(PKCS12KeyInfo keyInfo, Password password, + SEQUENCE encSafeContents) throws Exception { +- PrivateKey k = keyInfo.getPrivateKey(); +- if (k == null) { +- logger.debug("NO PRIVATE KEY for " + keyInfo.subjectDN); +- return; +- } +- + logger.debug("Creating key bag for " + keyInfo.subjectDN); + +- PasswordConverter passConverter = new PasswordConverter(); +- byte[] epkiBytes = CryptoManager.getInstance() +- .getInternalKeyStorageToken() +- .getCryptoStore() +- .getEncryptedPrivateKeyInfo( +- /* NSS has a bug that causes any AES CBC encryption +- * to use AES-256, but AlgorithmID contains chosen +- * alg. To avoid mismatch, use AES_256_CBC. */ +- passConverter, password, EncryptionAlgorithm.AES_256_CBC, 0, k); ++ byte[] epkiBytes = keyInfo.getEncryptedPrivateKeyInfoBytes(); ++ if (epkiBytes == null) { ++ PrivateKey k = keyInfo.getPrivateKey(); ++ if (k == null) { ++ logger.debug("NO PRIVATE KEY for " + keyInfo.subjectDN); ++ return; ++ } ++ logger.debug("Encrypting private key for " + keyInfo.subjectDN); ++ ++ PasswordConverter passConverter = new PasswordConverter(); ++ epkiBytes = CryptoManager.getInstance() ++ .getInternalKeyStorageToken() ++ .getCryptoStore() ++ .getEncryptedPrivateKeyInfo( ++ /* NSS has a bug that causes any AES CBC encryption ++ * to use AES-256, but AlgorithmID contains chosen ++ * alg. To avoid mismatch, use AES_256_CBC. */ ++ passConverter, password, ++ EncryptionAlgorithm.AES_256_CBC, 0, k); ++ } + + SET keyAttrs = createKeyBagAttrs(keyInfo); + diff --git a/pki-core-Fix-token-enrollment-and-recovery-ivs.patch b/pki-core-Fix-token-enrollment-and-recovery-ivs.patch new file mode 100644 index 0000000..36f26b1 --- /dev/null +++ b/pki-core-Fix-token-enrollment-and-recovery-ivs.patch @@ -0,0 +1,31 @@ +commit a91b457abfd61c39e1e4318c2443e38b2dd93c5c +Author: Ade Lee +Date: Fri Jun 16 19:25:05 2017 -0400 + + Fix token enrollment and recovery ivs + + In encryption mode, the archival of the geenrated key uses the + wrapIV, while the recovery uses the encryptIV. To make sure + these are consistent, they need to be set to be the same. + + Bugzilla BZ #1458043 + + Change-Id: I1ecece74bd6e486c0f37b5e1df4929744fac262b + +diff --git a/base/kra/src/com/netscape/kra/NetkeyKeygenService.java b/base/kra/src/com/netscape/kra/NetkeyKeygenService.java +index 96d7aae..07333b7 100644 +--- a/base/kra/src/com/netscape/kra/NetkeyKeygenService.java ++++ b/base/kra/src/com/netscape/kra/NetkeyKeygenService.java +@@ -406,6 +406,12 @@ public class NetkeyKeygenService implements IService { + + try { + params = mStorageUnit.getWrappingParams(allowEncDecrypt_archival); ++ ++ // In encrypt mode, the recovery side is doing a decrypt() using the ++ // encryption IV. To be sure this is successful, we will make sure' ++ // the IVs are the same. ++ params.setPayloadEncryptionIV(params.getPayloadWrappingIV()); ++ + privateKeyData = mStorageUnit.wrap((org.mozilla.jss.crypto.PrivateKey) privKey, params); + } catch (Exception e) { + request.setExtData(IRequest.RESULT, Integer.valueOf(4)); diff --git a/pki-core.spec b/pki-core.spec index b2e1f74..8f91cf6 100644 --- a/pki-core.spec +++ b/pki-core.spec @@ -68,8 +68,8 @@ Name: pki-core Version: 10.4.1 Release: 8%{?dist} %else -Version: 10.4.7 -Release: 1%{?dist} +Version: 10.4.8 +Release: 2%{?dist} %endif Summary: Certificate System - PKI Core Components URL: http://pki.fedoraproject.org/ @@ -234,6 +234,14 @@ Source0: http://pki.fedoraproject.org/pki/sources/%{name}/%{version}/%{ Source0: http://pki.fedoraproject.org/pki/sources/%{name}/%{version}/%{release}/%{name}-%{version}%{?prerel}.tar.gz %endif +####################### +## pki-core-10.4.8-2 +####################### +Patch0: pki-core-Fix-3DES-archival.patch +Patch1: pki-core-Fix-token-enrollment-and-recovery-ivs.patch +Patch2: pki-core-CMC-check-HTTPS-client-authentication-cert.patch +Patch3: pki-core-Fix-regression-in-pkcs12-key-bag-creation.patch + # Obtain version phase number (e. g. - used by "alpha", "beta", etc.) # # NOTE: For "alpha" releases, will be ".a1", ".a2", etc. @@ -897,6 +905,10 @@ This package is a part of the PKI Core used by the Certificate System. %prep %setup -q -n %{name}-%{version}%{?prerel} +%patch0 -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 %clean %{__rm} -rf %{buildroot} @@ -1422,6 +1434,30 @@ fi %endif # %{with server} %changelog +* Mon Jun 19 2017 Dogtag Team 10.4.8-2 +- dogtagpki Pagure Issue #2721 - Key recovery using externalReg fails + with java null pointer exception on KRA (vakwetu) +- dogtagpki Pagure Issue #2737 - CMC: check HTTPS client + authentication cert against CMC signer (cfu) +- dogtagpki Pagure Issue #2741 - Unable to find keys in the p12 file + after deleting the any of the subsystem certs from it (ftweedal) + +* Mon Jun 12 2017 Dogtag Team 10.4.8-1 +- dogtagpki Pagure Issue #2540 - Creating symmetric key (sharedSecret) + using tkstool is failing when operating system is in FIPS mode. (jmagne) +- dogtagpki Pagure Issue #2617 - Allow CA to process pre-signed CMC + non-signing certificate requests (cfu) +- dogtagpki Pagure Issue #2619 - Allow CA to process pre-signed CMC + revocation non-signing cert requests (cfu) +- dogtagpki Pagure Issue #2643 - Session timeout for PKI console + (edewata) +- dogtagpki Pagure Issue #2719 - change the way aes clients refer to + aes keysets (vakwetu) +- dogtagpki Pagure Issue #2722 - dont reuse IVs in the CMC code + (vakwetu) +- dogtagpki Pagure Issue #2728 - In keywrap mode, key recovery on + KRA with HSM causes KRA to crash (ftweedal) + * Mon Jun 5 2017 Dogtag Team 10.4.7-1 - Require "selinux-policy-targeted >= 3.13.1-159" as a runtime requirement - Require "tomcatjss >= 7.2.3" as a build and runtime requirement diff --git a/sources b/sources index 8f8899e..79db1b3 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (pki-core-10.4.7.tar.gz) = 896faf5e4b7c50f168e921004625fee3041bb526fde5bdbac7c7191d368f61495cf8b35bcbc05a1685e4220370c1bee178ac28d459be123bbb61b1834015260f +SHA512 (pki-core-10.4.8.tar.gz) = 109607d222bdd46605e96b0afbefce9b92d374a721eb4e6e562444f3816a9ca9632cc5481c1f05a51455cdf5ead9476bc9a1cc670934a235976583d71b7aca1d