From da1390ecda0a3a7626aa64a2e9ff4e55fe562bc0 Mon Sep 17 00:00:00 2001 From: Marco Fargetta Date: Fri, 19 Jun 2026 13:04:53 +0200 Subject: [PATCH] Use parent CA key type for LWCA generation Previously, lightweight CAs always generated RSA keys regardless of the parent CA's key type. This change makes LWCA key generation match the parent CA's algorithm and strength. Changes: - Detect parent CA key type (RSA, EC, ML-DSA) and generate matching key pair for lightweight CA - Use appropriate signature algorithm in PKCS10 request based on key type instead of hardcoded SHA256withRSA - Add key parameter constraints to caCACert profile - Maintain backward compatibility with fallback to RSA 3072 Assisted-by: Claude Sonnet 4.5 --- base/ca/shared/profiles/ca/caCACert.cfg | 2 +- .../com/netscape/ca/CertificateAuthority.java | 73 +++++++++++++++++-- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/base/ca/shared/profiles/ca/caCACert.cfg b/base/ca/shared/profiles/ca/caCACert.cfg index 4b4fcaaa05e..7133762f98b 100644 --- a/base/ca/shared/profiles/ca/caCACert.cfg +++ b/base/ca/shared/profiles/ca/caCACert.cfg @@ -30,7 +30,7 @@ policyset.caCertSet.2.default.params.startTime=0 policyset.caCertSet.3.constraint.class_id=keyConstraintImpl policyset.caCertSet.3.constraint.name=Key Constraint policyset.caCertSet.3.constraint.params.keyType=- -policyset.caCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp384,nistp521 +policyset.caCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp384,nistp521,44,65,87 policyset.caCertSet.3.default.class_id=userKeyDefaultImpl policyset.caCertSet.3.default.name=Key Default policyset.caCertSet.4.constraint.class_id=noConstraintImpl diff --git a/base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java b/base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java index 37426d05cc6..fbd2a39102f 100644 --- a/base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java +++ b/base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java @@ -27,12 +27,15 @@ import java.security.KeyPair; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.CertificateParsingException; import java.security.interfaces.RSAKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.NamedParameterSpec; import java.util.Arrays; import java.util.Collection; import java.util.Date; @@ -57,6 +60,7 @@ import org.mozilla.jss.crypto.KeyPairGenerator; import org.mozilla.jss.crypto.NoSuchItemOnTokenException; import org.mozilla.jss.crypto.ObjectNotFoundException; +import org.mozilla.jss.crypto.PrivateKey.Type; import org.mozilla.jss.crypto.SignatureAlgorithm; import org.mozilla.jss.crypto.TokenException; import org.mozilla.jss.crypto.X509Certificate; @@ -73,8 +77,11 @@ 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.pkcs11.PK11PrivKey; import org.mozilla.jss.pkix.cert.Extension; import org.mozilla.jss.pkix.primitive.Name; +import org.mozilla.jss.util.ECCurve; +import org.mozilla.jss.util.ECOIDs; import com.netscape.certsrv.authority.IAuthority; import com.netscape.certsrv.base.EBaseException; @@ -1442,19 +1449,62 @@ public Collection getAuthorityKeyHosts() { public KeyPair generateKeyPair(CryptoToken token) throws Exception { + KeyPairGenerator gen = null; + + PrivateKey thisPriv = mSigningUnit.getPrivateKey(); + + if (thisPriv instanceof PK11PrivKey pKey) { + if (pKey.getType() == Type.RSA) { + logger.info("CertificateAuthority: generating RSA key"); + gen = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); + gen.initialize(pKey.getStrength()); + return gen.genKeyPair(); + } + if (pKey.getType() == Type.EC) { + logger.info("CertificateAuthority: generating EC key"); + gen = token.getKeyPairGenerator(KeyPairAlgorithm.EC); + String curveName = null; + try { + X509CertImpl caCertImpl = mSigningUnit.getCertImpl(); + if (caCertImpl != null) { + X509Key caPubKey = (X509Key) caCertImpl.get(X509CertImpl.PUBLIC_KEY); + java.util.Vector curves = CryptoUtil.getECKeyCurve(caPubKey); + if (curves != null && !curves.isEmpty()) { + curveName = curves.firstElement(); + } + } + } catch (Exception e) { + logger.warn("CertificateAuthority: failed to get EC curve name from parent certificate: " + e.getMessage()); + } + if (curveName != null) { + gen.initialize(gen.getCurveCodeByName(curveName)); + } else { + gen.initialize(new ECGenParameterSpec(ECCurve.P384.getNames()[0])); + } + return gen.genKeyPair(); + } + if (pKey.getType() == Type.MLDSA44 || + pKey.getType() == Type.MLDSA65 || + pKey.getType() == Type.MLDSA87) { + logger.info("CertificateAuthority: generating MLDSA key"); + gen = token.getKeyPairGenerator(KeyPairAlgorithm.MLDSA); + gen.initialize(new NamedParameterSpec(pKey.getType().toString())); + return gen.genKeyPair(); + } + } + + PublicKey thisPub = mSigningUnit.getPublicKey(); logger.info("CertificateAuthority: generating RSA key"); // Key size of sub-CA shall be key size of this CA. // If the key is not RSA (e.g. EC) default to 3072 bits. // TODO: key generation parameters - KeyPairGenerator gen = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); + gen = token.getKeyPairGenerator(KeyPairAlgorithm.RSA); int keySize = 3072; - PublicKey thisPub = mSigningUnit.getPublicKey(); if (thisPub instanceof RSAKey) { keySize = ((RSAKey) thisPub).getModulus().bitLength(); } gen.initialize(keySize); - return gen.genKeyPair(); } @@ -1466,9 +1516,22 @@ public PKCS10 generateCertRequest( PublicKey pub = keypair.getPublic(); X509Key x509key = CryptoUtil.createX509Key(pub); - PKCS10 pkcs10 = new PKCS10(x509key); - Signature signature = Signature.getInstance("SHA256withRSA"); + PrivateKey priv = keypair.getPrivate(); + Signature signature = switch (priv.getAlgorithm()) { + case "RSA" -> Signature.getInstance("SHA256withRSA"); + case "EC" -> Signature.getInstance("SHA256withECDSA"); + case "ML-DSA" -> { + if (priv instanceof PK11PrivKey pKey) { + yield Signature.getInstance(pKey.getType().toString()); + } + throw new ECAException("ML-DSA key not handled by JSS"); + } + default -> { + logger.warn("Private key type not recognised. Try with SHA256withRSA signature"); + yield Signature.getInstance("SHA256withRSA"); + } + }; signature.initSign(keypair.getPrivate()); pkcs10.encodeAndSign(new X500Signer(signature, subjectX500Name));