-
Notifications
You must be signed in to change notification settings - Fork 162
Use parent CA key type for LWCA generation #5393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<String> getAuthorityKeyHosts() { | |
|
|
||
| public KeyPair generateKeyPair(CryptoToken token) throws Exception { | ||
|
|
||
| KeyPairGenerator gen = null; | ||
|
|
||
| PrivateKey thisPriv = mSigningUnit.getPrivateKey(); | ||
|
|
||
| if (thisPriv instanceof PK11PrivKey pKey) { | ||
|
edewata marked this conversation as resolved.
|
||
| 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<String> 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()); | ||
| } | ||
|
Comment on lines
+1476
to
+1478
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it OK to ignore this type of failure, or should we propagate the exception to the caller so that the operation fails and the user will know that there's something wrong?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is in-line with RSA where there is a default in case when the original parent value cannot be retrieved. Since it is not possible to modify the value without creating a new CA I think the operation should not be stopped. At least a LWCA is created and can be used although the certificate could not have the proper algorithm. Maybe, if the option to customise LWCA will be added then this should fail.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC the new code will try to get the curve name from the signing cert and return a new EC key with the same curve. However, if there's a cert parsing issue or a bug in the code it will silently default to P-384. I think ideally the user should be notified (by throwing an exception) if there's something wrong so they won't end up with a LWCA that they don't want/can't use which then they have to remove and create again, but I'll let you decide. |
||
| if (curveName != null) { | ||
| gen.initialize(gen.getCurveCodeByName(curveName)); | ||
| } else { | ||
| gen.initialize(new ECGenParameterSpec(ECCurve.P384.getNames()[0])); | ||
| } | ||
| return gen.genKeyPair(); | ||
| } | ||
|
fmarco76 marked this conversation as resolved.
|
||
| 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(); | ||
|
Comment on lines
+1486
to
+1492
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔴 Critical 🧩 Analysis chain🌐 Web query:
💡 Result: In Mozilla JSS (org.mozilla.jss), the class org.mozilla.jss.crypto.PrivateKey.Type is an enumeration-like structure used to represent key algorithms [1]. While recent development work in Mozilla's NSS (which underlies JSS) has added support for ML-DSA [2][3], the string returned by org.mozilla.jss.crypto.PrivateKey.Type.toString for these specific types is "ML-DSA" (or potentially a variant such as "MLDSA" depending on the exact implementation of the enum's string representation). Specifically, internal NSS changes associated with ML-DSA have introduced a key type referred to as mldsaKey [2]. Regarding the Java Development Kit (JDK) standard NamedParameterSpec names, the standard names for the ML-DSA (FIPS 204) parameter sets are: - ML-DSA-44 - ML-DSA-65 - ML-DSA-87 These names are utilized in the Java security ecosystem (e.g., in providers like Bouncy Castle) to identify these post-quantum cryptographic algorithms [4]. Note that ML-DSA is a FIPS-standardized algorithm derived from CRYSTALS-Dilithium [5][6]. Implementations typically use these strings when creating a NamedParameterSpec to specify the elliptic curve or parameter set in cryptographic operations. Citations:
🏁 Script executed: # Search for JSS PrivateKey.Type enum definition
rg -t java "enum\s+Type|class\s+Type" --search-path . -A 20 | head -100Repository: dogtagpki/pki Length of output: 595 🏁 Script executed: # Search for MLDSA enum constants and any toString overrides in JSS
rg -t java "MLDSA44|MLDSA65|MLDSA87" --search-path . -B 5 -A 5Repository: dogtagpki/pki Length of output: 595 🌐 Web query:
💡 Result: As of June 2026, Java Security Services (JSS) is in the process of adding support for the ML-DSA (Module-Lattice-Based Digital Signature Algorithm) signature scheme, as evidenced by recent development activity in the Mozilla NSS and JSS ecosystem (Bug 1983320) [1][2][3][4]. While the JSS Citations:
🏁 Script executed: # Find the PrivateKey.Type enum file
fd -t f "PrivateKey.java"Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Search for Type enum definition in the repository
rg -t java "MLDSA44|MLDSA65|MLDSA87"Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Look for any toString implementations related to Type
rg -t java "class Type|enum Type" -A 50 | grep -E "MLDSA|toString|enum Type"Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Search for PrivateKey usage and imports
rg -t java "import.*PrivateKey"Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Search for pKey usage patterns in the target file
sed -n '1480,1500p' base/ca/src/main/java/com/netscape/ca/CertificateAuthority.javaRepository: dogtagpki/pki Length of output: 1111 🏁 Script executed: # Look for build files to identify JSS version
fd -t f "pom.xml|build.gradle" | head -5Repository: dogtagpki/pki Length of output: 243 🏁 Script executed: # Check if JSS is vendored or available in the repository
find . -name "jss*" -type d 2>/dev/null | head -10Repository: dogtagpki/pki Length of output: 195 🏁 Script executed: # Check JSS version in the CA pom.xml
cat base/ca/pom.xml | grep -A 5 -B 5 "jss"Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Look for any Type enum or MLDSA references in the codebase
rg -t java "Type\.(MLDSA|RSA|EC)" | head -20Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Search for NamedParameterSpec usage patterns
rg -t java "NamedParameterSpec" -B 3 -A 3Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Read the CertificateAuthority.java file around the MLDSA code block
cat -n base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java | sed -n '1,100p' | grep -i "import\|package"Repository: dogtagpki/pki Length of output: 4387 🏁 Script executed: # Get imports from the file
head -50 base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java | grep "import"Repository: dogtagpki/pki Length of output: 1281 🏁 Script executed: # Search for Type enum reference definition
rg "Type\.MLDSA" -B 5 -A 5Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Check pom.xml for JSS dependency
cat base/ca/pom.xmlRepository: dogtagpki/pki Length of output: 1749 🏁 Script executed: # Check the parent pom.xml for JSS dependency
cat base/pom.xml | grep -A 10 -B 10 "jss\|JSS"Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Check pki-server pom.xml for JSS dependency
cat base/server/pom.xml | grep -A 5 -B 5 "jss\|JSS" | head -50Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Look for Type enum definition in JSS source or check if there's documentation
rg "enum Type" -t java -A 30 | head -100Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Check root pom.xml
cat pom.xml | grep -A 5 -B 5 "jss\|JSS" | head -50Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # List the directory structure to understand the project layout
ls -la base/ | head -20Repository: dogtagpki/pki Length of output: 1254 🏁 Script executed: # Search for all pom.xml files and check them for JSS
find . -name "pom.xml" -exec grep -l "jss\|JSS" {} \;Repository: dogtagpki/pki Length of output: 248 🏁 Script executed: # Check the common pom.xml for JSS dependency
cat base/common/pom.xml | grep -A 10 -B 5 "jss\|JSS"Repository: dogtagpki/pki Length of output: 683 🏁 Script executed: # Also check if there's a dependencyManagement section in root or parent pom
cat base/pom.xmlRepository: dogtagpki/pki Length of output: 1209 🏁 Script executed: # Look for CMakeLists.txt which might define JSS version
grep -i "jss" base/CMakeLists.txt | head -20Repository: dogtagpki/pki Length of output: 429 🏁 Script executed: # Search for any Type enum constants and their usage patterns in the codebase
rg "Type\.(RSA|EC|DSA|MLDSA)" -t java | head -30Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Look for other uses of Type.toString() in the code
rg "\.getType\(\)\.toString\(\)" -t java -B 3 -A 3Repository: dogtagpki/pki Length of output: 151 🏁 Script executed: # Check if there are any test files that show how MLDSA is used
find . -path "*/test/*" -name "*.java" -exec grep -l "MLDSA\|NamedParameterSpec" {} \; | head -10Repository: dogtagpki/pki Length of output: 151 🌐 Web query:
💡 Result: In the Java Security Services (JSS) library, the org.mozilla.jss.crypto.PrivateKey.Type class is used to represent the type of a private key (e.g., RSA, DSA, EC) [1][2]. The toString method for this class is implemented to return a string representation of the algorithm, such as "RSA", "DSA", or "EC" [1][2]. As of JSS 5.10.0, there is no explicit MLDSA (Module-Lattice-Based Digital Signature Algorithm) value or specific toString implementation for MLDSA within the org.mozilla.jss.crypto.PrivateKey.Type class [1][2]. While JSS and its underlying Network Security Services (NSS) have been updated to support ML-DSA keys and mechanisms in the PKCS#11 interface [3], this support is handled through low-level C libraries (NSS/freebl) [4] and PKCS#11 mappings [3], rather than through the legacy PrivateKey.Type enum in the Java layer [1][5]. Users looking for ML-DSA functionality in Java are typically using other cryptographic providers, such as Bouncy Castle, which provides its own MLDSA class and implementation [6][7][8][9][10], or IAIK_PQ [11], which implement the standard independently. If you encounter an object representing an ML-DSA key in JSS, it does not utilize the PrivateKey.Type.toString method for this algorithm [1][3]. Citations:
🌐 Web query:
💡 Result: In the Java Security API, the ML-DSA (Module-Lattice-Based Digital Signature Algorithm) parameter sets are represented by the java.security.spec.NamedParameterSpec class [1][2]. These parameters were introduced in JDK 24 [3][4]. The standard ML-DSA parameter names, as defined in FIPS 204 [5][6] and incorporated into the Java Security Standard Algorithm Names Specification [7], are: - ML-DSA-44: Represented by the constant NamedParameterSpec.ML_DSA_44 [2][3]. - ML-DSA-65: Represented by the constant NamedParameterSpec.ML_DSA_65 [2][3]. - ML-DSA-87: Represented by the constant NamedParameterSpec.ML_DSA_87 [2][3]. These constants are static final members of the NamedParameterSpec class [2][8]. When using these parameters with APIs such as KeyPairGenerator, the standard name strings (e.g., "ML-DSA-44") or the corresponding NamedParameterSpec constants can be used to initialize the generator [1][9]. Regardless of the specific parameter set used, the family name for the algorithm in the Java Security API is "ML-DSA" [1][9]. Citations:
Use Example fixString paramName = switch(pKey.getType()) {
case MLDSA44 -> "ML-DSA-44";
case MLDSA65 -> "ML-DSA-65";
case MLDSA87 -> "ML-DSA-87";
default -> throw new IllegalArgumentException("Unsupported MLDSA type");
};
gen.initialize(new NamedParameterSpec(paramName));🤖 Prompt for AI Agents |
||
| } | ||
|
fmarco76 marked this conversation as resolved.
|
||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC we can't use ML-KEM for signing, so should we throw something like "Unsupported key alg" at the end of
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check on private key of parent CA so I am expecting it is not ML-KEM. If a new key type is introduced the LWCA should work with RSA default, at least until this because configurable.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, I'm thinking we should reject the new alg if we don't have the code to handle it rather than defaulting to RSA which they might not want or can't use, but I'll let you decide. |
||
|
|
||
| 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(); | ||
| } | ||
|
Comment on lines
1503
to
1506
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's true that all private keys are instances of
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only case this code is executed when a new key type is introduced. As discussed above it is to make it working always. This should be configurable to allow failing and change configuration. |
||
| 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"); | ||
|
Comment on lines
+1522
to
+1523
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The hash alg is hard-coded here. Should we use a hash param? |
||
| case "ML-DSA" -> { | ||
| if (priv instanceof PK11PrivKey pKey) { | ||
| yield Signature.getInstance(pKey.getType().toString()); | ||
| } | ||
| throw new ECAException("ML-DSA key not handled by JSS"); | ||
| } | ||
|
fmarco76 marked this conversation as resolved.
Comment on lines
+1521
to
+1529
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔴 Critical 🧩 Analysis chain🌐 Web query:
💡 Result: In Mozilla JSS (Java Security Services), PK11PrivKey.getAlgorithm for an ML-DSA private key returns the string "ML-DSA" [1]. Regarding java.security.Signature.getInstance, the algorithm name "ML-DSA-65" (or other parameter-set variants like "ML-DSA-44" and "ML-DSA-87") is a valid, standard algorithm name defined for Java security APIs [2][3]. When you call Signature.getInstance("ML-DSA-65"), it specifically requires that the key used for the operation also conforms to the ML-DSA-65 parameter set; attempting to use an incompatible key will result in an InvalidKeyException [2]. Alternatively, you can use the family name "ML-DSA", which accepts keys of any ML-DSA parameter set [2]. These naming conventions are part of the Java standard algorithm names established for quantum-resistant algorithms, consistent with FIPS 204 [4][2][3]. Citations:
🏁 Script executed: #!/bin/bash
# Search for JSS PrivateKey.Type or PK11PrivKey.Type implementation in the codebase
# to see what toString() returns for ML-DSA keys
# First, check if JSS source or references are in this repo
fd -t f -e java | head -20
# Look for any ML-DSA related type definitions
rg -i "MLDSA|ML-DSA" --type java | head -30
# Search for Type enum or class definitions
rg "Type\." --type java | grep -i "mldsa\|ml-dsa" | head -20Repository: dogtagpki/pki Length of output: 1688 🌐 Web query:
💡 Result: In the context of Mozilla's Java Security Services (JSS) and the underlying Network Security Services (NSS) library, the ML-DSA (Module-Lattice-Based Digital Signature Algorithm) support has been introduced via Bug 1983320 [1][2]. While the standard PrivateKey.Type.toString method in JSS traditionally returns strings like "RSA", "DSA", or "EC" [3][4], the integration of ML-DSA into the NSS/JSS architecture typically utilizes OIDs (Object Identifiers) and specialized parameter sets to distinguish between variants such as ML-DSA-44, ML-DSA-65, and ML-DSA-87 [2][5]. For ML-DSA specifically, the internal representation uses the OID-based identifiers (e.g., SEC_OID_ML_DSA_65) [2]. When accessing key type information through the updated NSS interfaces, the system maps these to specific mechanisms and key types rather than a simple string. As JSS provides a Java wrapper around NSS, if you were to invoke a string representation for an ML-DSA type, it would be expected to return the identifier string, which is typically "ML-DSA-65" (or "ML-DSA-44"/"ML-DSA-87") in line with standard identifier naming conventions used in the updated NSS command-line and printing utilities [5]. Because the support for ML-DSA is a recent addition involving structural changes to how key types are mapped and printed [1][6][5], you should ensure your JSS environment is built against an NSS version that includes the commits associated with Bug 1983320. In these updated versions, the printing and identification logic has been extended to recognize these OIDs explicitly [5]. Citations:
🌐 Web query:
💡 Result: In the Java Security Services (JSS) library, the org.mozilla.jss.crypto.PrivateKey.Type class is a type-safe enum used to identify the algorithm type of a private key [1][2]. As of the available documentation, this class contains defined fields for legacy algorithms such as RSA, DSA, and EC (Elliptic Curve), but it does not include a native field or specific constant for ML-DSA [1][2]. While ML-DSA (Module-Lattice-Based Digital Signature Algorithm) is a standardized, quantum-resistant algorithm supported by modern Java platforms (such as via JEP 497 in the OpenJDK) [3][4][5], JSS operates as a distinct cryptographic provider. If you encounter an ML-DSA key within a JSS context, it is not represented by a standard PrivateKey.Type constant [1][2]. If you attempt to call toString on a hypothetical or unsupported ML-DSA type object in JSS, it would not return a standardized "ML-DSA" string value managed by the PrivateKey.Type class, as that class is currently limited to the hardcoded algorithm types defined during its implementation [1][2]. For native ML-DSA operations in Java, developers should use the standard Java Cryptography Architecture (JCA) APIs, such as KeyPairGenerator and Signature, which now define ML-DSA, ML-DSA-44, ML-DSA-65, and ML-DSA-87 as standard algorithm names [3][5]. Citations:
The ML-DSA branch has a critical compatibility issue: JSS PrivateKey.Type does not currently support ML-DSA. While 🤖 Prompt for AI Agents |
||
| default -> { | ||
| logger.warn("Private key type not recognised. Try with SHA256withRSA signature"); | ||
| yield Signature.getInstance("SHA256withRSA"); | ||
|
Comment on lines
+1531
to
+1532
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we throw an exception instead? I suppose people want to use a specific key alg instead of defaulting to something that might not work for their system. |
||
| } | ||
| }; | ||
|
fmarco76 marked this conversation as resolved.
|
||
| signature.initSign(keypair.getPrivate()); | ||
| pkcs10.encodeAndSign(new X500Signer(signature, subjectX500Name)); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.