Skip to content

Commit

Permalink
Bug 2246422 ServerSideKeygen static SKID (#4597)
Browse files Browse the repository at this point in the history
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

Co-authored-by: Christina Fu <[email protected]>
  • Loading branch information
ladycfu and Christina Fu authored Nov 8, 2023
1 parent 71bf161 commit efb1aa7
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@
import org.dogtagpki.server.ca.ICertificateAuthority;
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.CertificateAuthority;
Expand Down Expand Up @@ -292,31 +297,68 @@ public void execute(IRequest request)

// process certificate issuance
X509CertInfo info = request.getExtDataInCertInfo(REQUEST_CERTINFO);
logger.debug(method + "cfu before: X509CertInfo info = " + info.toString());

if (isSSKeygen) {
try {
String pubKeyStr = request.getExtDataInString("public_key");
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) {

// 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 relaced;
// 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.debug(method + e);
throw new EProfileException(e);
} catch (CertificateException e) {
logger.debug(method + e);
throw new EProfileException(e);
} catch (Exception e) {
logger.debug(method + e);
throw new EProfileException(e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ else if (keyType.contentEquals("EC")) {
request.setExtData(IRequest.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());
Expand Down
120 changes: 120 additions & 0 deletions base/server/src/main/java/com/netscape/cmscore/cert/CertUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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.Iterator;
import java.util.Locale;
Expand Down Expand Up @@ -569,6 +571,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 EProfileException {
if (ext == null) {
throw new EProfileException("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 EProfileException(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 EProfileException("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<String> names = new ArrayList<>();
Enumeration<String> 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 EProfileException {
try {
deleteExtension(name, info);
} catch (Exception e) {
throw new EProfileException(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<Extension> 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();

Expand Down

0 comments on commit efb1aa7

Please sign in to comment.