From 4e42191d43aee9516ba2e994e2e90d3ae68873dc Mon Sep 17 00:00:00 2001 From: Christina Fu Date: Wed, 8 Nov 2023 09:16:09 -0800 Subject: [PATCH] Bug 2246422 ServerSideKeygen static SKID (#4597) This patch intends to address the bug where in the case of ServerSide keygen, a static SKI extension was injected as a result. fixes https://bugzilla.redhat.com/show_bug.cgi?id=2246422 --- .../cms/profile/common/CAEnrollProfile.java | 53 +++++++- .../def/ServerKeygenUserKeyDefault.java | 1 + .../com/netscape/cmscore/cert/CertUtils.java | 120 ++++++++++++++++++ 3 files changed, 167 insertions(+), 7 deletions(-) diff --git a/base/ca/src/main/java/com/netscape/cms/profile/common/CAEnrollProfile.java b/base/ca/src/main/java/com/netscape/cms/profile/common/CAEnrollProfile.java index 2cc8ae5d5e7..010f584b945 100644 --- a/base/ca/src/main/java/com/netscape/cms/profile/common/CAEnrollProfile.java +++ b/base/ca/src/main/java/com/netscape/cms/profile/common/CAEnrollProfile.java @@ -26,9 +26,14 @@ import org.dogtagpki.server.ca.CAEngine; import org.mozilla.jss.netscape.security.x509.CertificateSubjectName; import org.mozilla.jss.netscape.security.x509.CertificateX509Key; +import org.mozilla.jss.netscape.security.x509.Extension; +import org.mozilla.jss.netscape.security.x509.KeyIdentifier; +import org.mozilla.jss.netscape.security.x509.PKIXExtensions; +import org.mozilla.jss.netscape.security.x509.SubjectKeyIdentifierExtension; import org.mozilla.jss.netscape.security.x509.X500Name; import org.mozilla.jss.netscape.security.x509.X509CertImpl; import org.mozilla.jss.netscape.security.x509.X509CertInfo; +import org.mozilla.jss.netscape.security.x509.X509Key; import org.mozilla.jss.pkix.crmf.PKIArchiveOptions; import com.netscape.ca.CAService; @@ -282,6 +287,7 @@ public void execute(Request request) throws EProfileException, ERejectException // process certificate issuance X509CertInfo info = request.getExtDataInCertInfo(Request.REQUEST_CERTINFO); + // logger.debug(method + " before: X509CertInfo info = " + info.toString()); if (isSSKeygen) { try { @@ -290,21 +296,51 @@ public void execute(Request request) throws EProfileException, ERejectException if (pubKeyStr == null) { throw new EProfileException("Server-Side Keygen enrollment failed to retrieve public_key from KRA"); } - - //logger.debug(method + "pubKeyStr = " + pubKeyStr); + // logger.debug(method + "pubKeyStr = " + pubKeyStr); byte[] pubKeyB = CryptoUtil.base64Decode(pubKeyStr); - CertificateX509Key certKey = new CertificateX509Key(new ByteArrayInputStream(pubKeyB)); - Object oj = info.get(X509CertInfo.KEY); - - if (oj != null) { + CertificateX509Key certKey = new CertificateX509Key( + new ByteArrayInputStream(pubKeyB)); + + // replace fake key in info + CertificateX509Key infokey = (CertificateX509Key) + info.get(X509CertInfo.KEY); + if (infokey != null) { + X509Key key = (X509Key) + infokey.get(CertificateX509Key.KEY); + // logger.debug(method + "key = " + key.toString()); // a placeholder temporary fake key was put in // ServerKeygenUserKeyDefault info.delete(X509CertInfo.KEY); - //logger.debug(method + " fake key deleted"); + // logger.debug(method + "key deleted"); } + // adding real key info.set(X509CertInfo.KEY, certKey); + // fake key replaced; + // need to compute/replace SKI as well if present + + Extension ext = CertUtils.getExtension(PKIXExtensions.SubjectKey_Id.toString(), info); + if (ext != null) { + logger.debug(method + "found SubjectKey_Id extension"); + // compute keyId + X509Key realkey = (X509Key) + certKey.get(CertificateX509Key.KEY); + byte[] hash = CryptoUtil.generateKeyIdentifier(realkey.getKey()); + KeyIdentifier id = new KeyIdentifier(hash); + SubjectKeyIdentifierExtension skiExt = + new SubjectKeyIdentifierExtension(id.getIdentifier()); + + // replace it + CertUtils.replaceExtension(PKIXExtensions.SubjectKey_Id.toString(), skiExt, info); + logger.debug(method + " SubjectKey_Id replaced"); + + // logger.debug(method + " after replacement: X509CertInfo info = " + info.toString()); + }/* else + Not every cert needs an SKI + logger.debug(method + "did not find SubjectKey_Id"); + */ + } catch (IOException e) { logger.error(method + e.getMessage(), e); throw new EProfileException(e); @@ -312,6 +348,9 @@ public void execute(Request request) throws EProfileException, ERejectException } catch (CertificateException e) { logger.error(method + e.getMessage(), e); throw new EProfileException(e); + } catch (Exception e) { + logger.debug(method + e); + throw new EProfileException(e); } } diff --git a/base/ca/src/main/java/com/netscape/cms/profile/def/ServerKeygenUserKeyDefault.java b/base/ca/src/main/java/com/netscape/cms/profile/def/ServerKeygenUserKeyDefault.java index 4bdb8bcbb4c..4d865abe947 100644 --- a/base/ca/src/main/java/com/netscape/cms/profile/def/ServerKeygenUserKeyDefault.java +++ b/base/ca/src/main/java/com/netscape/cms/profile/def/ServerKeygenUserKeyDefault.java @@ -495,6 +495,7 @@ else if (keyType.contentEquals("EC")) { request.setExtData(Request.SERVER_SIDE_KEYGEN_ENROLL_ENABLE_ARCHIVAL, enableArchival? "true":"false"); info.set(X509CertInfo.KEY, certKey); + logger.debug(method + "fake key injected for SSK."); } catch (Exception e) { logger.debug("ServerKeygenUserKeyDefault: populate " + e.toString()); throw new EProfileException(e.getMessage()); diff --git a/base/server/src/main/java/com/netscape/cmscore/cert/CertUtils.java b/base/server/src/main/java/com/netscape/cmscore/cert/CertUtils.java index 1673d9a654c..4622b6ee747 100644 --- a/base/server/src/main/java/com/netscape/cmscore/cert/CertUtils.java +++ b/base/server/src/main/java/com/netscape/cmscore/cert/CertUtils.java @@ -31,6 +31,8 @@ import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; import java.util.Enumeration; import java.util.StringTokenizer; @@ -369,6 +371,124 @@ public static String getCertType(X509CertImpl cert) throws CertificateParsingExc return (sb.length() > 0) ? sb.toString() : null; } + public static void addExtension(String name, Extension ext, X509CertInfo info) + throws EBaseException { + if (ext == null) { + throw new EBaseException("addExtension: extension '" + name + "' is null"); + } + CertificateExtensions exts = null; + + Extension alreadyPresentExtension = getExtension(name, info); + + if (alreadyPresentExtension != null) { + String eName = ext.toString(); + logger.error("Duplicate extension: " + eName); + throw new EBaseException(CMS.getUserMessage("CMS_PROFILE_DUPLICATE_EXTENSION", eName)); + } + + try { + exts = (CertificateExtensions) + info.get(X509CertInfo.EXTENSIONS); + } catch (Exception e) { + logger.warn("EnrollDefault: " + e.getMessage(), e); + } + if (exts == null) { + throw new EBaseException("extensions not found"); + } + try { + exts.set(name, ext); + } catch (IOException e) { + logger.warn("EnrollDefault: " + e.getMessage(), e); + } + } + + public static void deleteExtension(String extID, X509CertInfo info) throws Exception { + + CertificateExtensions exts = (CertificateExtensions) info.get(X509CertInfo.EXTENSIONS); + + if (exts == null) { + return; + } + + Collection names = new ArrayList<>(); + Enumeration e = exts.getNames(); + + // get names of extensions to remove + while (e.hasMoreElements()) { + String name = e.nextElement(); + Extension ext = (Extension) exts.get(name); + + if (ext.getExtensionId().toString().equals(extID)) { + names.add(name); + } + } + + // remove extensions in separate loop to avoid ConcurrentModificationException + for (String name : names) { + exts.delete(name); + } + } + + public static void replaceExtension(String name, Extension ext, X509CertInfo info) + throws EBaseException { + try { + deleteExtension(name, info); + } catch (Exception e) { + throw new EBaseException(e); + } + + addExtension(name, ext, info); + } + + public static Extension getExtension(String name, X509CertInfo info) { + + if (info == null) { + logger.error("Missing certificate info"); + return null; + } + + CertificateExtensions exts = null; + + try { + exts = (CertificateExtensions) info.get(X509CertInfo.EXTENSIONS); + } catch (Exception e) { + logger.warn("EnrollDefault: getExtension " + e.getMessage(), e); + } + + if (exts == null) { + logger.debug("EnrollDefault: Unable to find extensions"); + return null; + } + + return getExtension(name, exts); + } + + public static Extension getExtension(String name, CertificateExtensions exts) { + + logger.debug("EnrollDefault: Searching for " + name + " extension"); + + if (exts == null) { + logger.error("Missing certificate extensions"); + return null; + } + + Enumeration e = exts.getAttributes(); + + logger.debug("EnrollDefault: Extensions:"); + while (e.hasMoreElements()) { + Extension ext = e.nextElement(); + logger.debug("EnrollDefault: - " + ext.getExtensionId()); + + if (ext.getExtensionId().toString().equals(name)) { + logger.debug("EnrollDefault: Found extension " + name); + return ext; + } + } + + logger.debug("EnrollDefault: Extension " + name + " not found"); + return null; + } + public static String getNSExtensionInfo(NSCertTypeExtension nsExtn) { StringBuffer sb = new StringBuffer();