Use parent CA key type for LWCA generation - #5393
Conversation
📝 WalkthroughWalkthrough
ChangesMulti-algorithm CA cert support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces support for EC and ML-DSA key generation and certificate requests in the Certificate Authority, updating both the configuration profile and the implementation in CertificateAuthority.java. The review feedback identifies three critical runtime issues: first, initializing the EC key generator with key size instead of a curve code will fail in JSS; second, using pKey.getType().toString() directly for NamedParameterSpec and Signature.getInstance will throw exceptions because standard JCA names require hyphens (e.g., ML-DSA-44 instead of MLDSA44). Mapping these types to their correct JCA names and retrieving the proper curve code is required.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java (1)
1460-1464: EC key generation does not preserve the parent key's curve specification.The code uses
gen.initialize(pKey.getStrength()), which only specifies the key size in bits (e.g., 256, 384, 521). However, different EC curves can have the same bit size—for example, both P-256 and secp256k1 are 256-bit curves. The JSSKeyPairGeneratorfor EC requires the curve code, not just the bit size, to generate a key with the exact parent curve.Reference the pattern in
CryptoUtil.generateECCKeyPair()(base/common/src/main/java/com/netscape/cmsutil/crypto/CryptoUtil.java, lines 862-895), which properly extracts the curve name and callskeygen.getCurveCodeByName(curveName)before initializing. Extract the curve information from the parent EC key (castpKeytoPK11ECPrivateKeyand callgetParams()) and pass the curve code to ensure an exact curve match.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java` around lines 1460 - 1464, The EC key generation in the code block where pKey.getType() equals Type.EC only initializes the KeyPairGenerator with the bit strength via gen.initialize(pKey.getStrength()), which fails to preserve the specific EC curve from the parent key since multiple curves can share the same bit size. To fix this, cast pKey to PK11ECPrivateKey, extract the curve parameters by calling getParams(), retrieve the curve name from those parameters, obtain the corresponding curve code, and pass the curve code to the generator initialization instead of just the strength value. Follow the pattern demonstrated in CryptoUtil.generateECCKeyPair() which properly extracts curve information and uses the curve code for initialization to ensure the generated key matches the exact parent curve.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java`:
- Around line 1510-1514: The default case in the switch statement currently logs
a warning and yields SHA256withRSA as a fallback, which can cause silent
failures if the private key algorithm is incompatible with RSA. Instead of
logging and proceeding with a potentially incompatible algorithm, replace the
default case block (which currently contains logger.warn and yield
Signature.getInstance) with a throw statement that raises an exception with a
clear error message indicating the private key algorithm is unrecognized and
unsupported. This will fail fast at the point of detection rather than causing
cryptic errors during the signing operation.
---
Nitpick comments:
In `@base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java`:
- Around line 1460-1464: The EC key generation in the code block where
pKey.getType() equals Type.EC only initializes the KeyPairGenerator with the bit
strength via gen.initialize(pKey.getStrength()), which fails to preserve the
specific EC curve from the parent key since multiple curves can share the same
bit size. To fix this, cast pKey to PK11ECPrivateKey, extract the curve
parameters by calling getParams(), retrieve the curve name from those
parameters, obtain the corresponding curve code, and pass the curve code to the
generator initialization instead of just the strength value. Follow the pattern
demonstrated in CryptoUtil.generateECCKeyPair() which properly extracts curve
information and uses the curve code for initialization to ensure the generated
key matches the exact parent curve.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 79f5916a-b949-4947-924f-95f5c9a54ec3
📒 Files selected for processing (2)
base/ca/shared/profiles/ca/caCACert.cfgbase/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
|
edewata
left a comment
There was a problem hiding this comment.
Please see my comments below.
| } catch (Exception e) { | ||
| logger.warn("CertificateAuthority: failed to get EC curve name from parent certificate: " + e.getMessage()); | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| int keySize = 3072; | ||
| PublicKey thisPub = mSigningUnit.getPublicKey(); | ||
| if (thisPub instanceof RSAKey) { | ||
| keySize = ((RSAKey) thisPub).getModulus().bitLength(); | ||
| } |
There was a problem hiding this comment.
If it's true that all private keys are instances of PK11PrivKey then this code might never be executed anymore. Should we move this code into the pKey.getType() == Type.RSA case above?
There was a problem hiding this comment.
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(new NamedParameterSpec(pKey.getType().toString())); | ||
| return gen.genKeyPair(); | ||
| } | ||
| } |
There was a problem hiding this comment.
IIUC we can't use ML-KEM for signing, so should we throw something like "Unsupported key alg" at the end of thisPriv instanceof PK11PrivKey case?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| logger.warn("Private key type not recognised. Try with SHA256withRSA signature"); | ||
| yield Signature.getInstance("SHA256withRSA"); |
There was a problem hiding this comment.
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.
| case "RSA" -> Signature.getInstance("SHA256withRSA"); | ||
| case "EC" -> Signature.getInstance("SHA256withECDSA"); |
There was a problem hiding this comment.
The hash alg is hard-coded here. Should we use a hash param?
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 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java`:
- Around line 1521-1529: The ML-DSA case in the switch statement for handling
private key algorithms does not properly construct a valid Signature algorithm
name because JSS's PrivateKey.Type enum does not yet support ML-DSA constants.
The call to pKey.getType().toString() will not produce the required format
(ML-DSA-65, ML-DSA-44, or ML-DSA-87). Instead of relying on pKey.getType(), you
should directly construct the proper Signature algorithm string for ML-DSA by
either extracting the key parameter set (if determinable from the ML-DSA key
itself) or using a reasonable default like ML-DSA-65, then pass that string to
Signature.getInstance(). Alternatively, if you cannot reliably determine the
parameter set, provide a more informative error message indicating that ML-DSA
support requires a JSS version with proper ML-DSA type support.
- Around line 1486-1492: The NamedParameterSpec initialization in the MLDSA key
generation block is using pKey.getType().toString() which produces format like
"MLDSA44", but JDK expects the standard format "ML-DSA-44" with hyphens and
uppercase DSA. Replace the pKey.getType().toString() call with a switch
statement that maps Type.MLDSA44 to "ML-DSA-44", Type.MLDSA65 to "ML-DSA-65",
and Type.MLDSA87 to "ML-DSA-87", then pass the correctly formatted string to the
NamedParameterSpec constructor.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2aac9968-1dfd-4690-9368-e2f10ea44ebd
📒 Files selected for processing (2)
base/ca/shared/profiles/ca/caCACert.cfgbase/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
✅ Files skipped from review due to trivial changes (1)
- base/ca/shared/profiles/ca/caCACert.cfg
| 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(); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical
🧩 Analysis chain
🌐 Web query:
In Mozilla JSS (org.mozilla.jss), what string does org.mozilla.jss.crypto.PrivateKey.Type.toString() return for the ML-DSA key types (MLDSA44/MLDSA65/MLDSA87), and what are the JDK standard NamedParameterSpec names for ML-DSA?
💡 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:
- 1: https://dogtagpki.github.io/jss/master/javadocs/org/mozilla/jss/crypto/PrivateKey.Type.html
- 2: https://phabricator.services.mozilla.com/D262393
- 3: https://searchfox.org/firefox-main/source/security/nss/lib/freebl/ml_dsa.c
- 4: https://downloads.bouncycastle.org/java/docs/bcprov-jdk15to18-javadoc/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.html
- 5: https://www.rfc-editor.org/info/rfc9881/
- 6: https://crates.io/crates/mldsa65
🏁 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:
JSS PrivateKey.Type enum MLDSA toString() implementation source code
💡 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 org.mozilla.jss.crypto.PrivateKey.Type class historically manages standard algorithm types like RSA, DSA, and EC [5][6], the toString() implementation in PrivateKey.Type is a method that returns a string representation of the algorithm [5][7]. The source code for this toString() method generally follows a pattern of returning the string name (e.g., "RSA", "DSA", "EC") associated with the constant [5][6]. In the context of the recent ML-DSA integration: - ML-DSA has been added to the NSS underlying native libraries as a key type (mldsaKey) and supporting OIDs [2][3]. - New definitions, such as "ML-DSA-44", "ML-DSA-65", and "ML-DSA-87", are being integrated into the system [2][3]. - The toString() implementation in PrivateKey.Type (and related JSS crypto classes) would be updated to return the corresponding string name (e.g., "ML-DSA") if and when it is explicitly added as a PrivateKey.Type constant [5][7]. Because the integration is ongoing, specific public source code for an MLDSA constant within the PrivateKey.Type class may not yet be present in stable, released JSS versions [5][6][7]. Developers tracking this should monitor the official JSS repository and the referenced Bug 1983320 on Mozilla's Phabricator for the final commit incorporating the MLDSA constant and its corresponding toString() logic [1][2].
Citations:
- 1: https://phabricator.services.mozilla.com/D262394
- 2: https://phabricator.services.mozilla.com/D262393
- 3: https://phabricator.services.mozilla.com/D262392
- 4: https://phabricator.services.mozilla.com/D262396
- 5: https://dogtagpki.github.io/jss/master/javadocs/org/mozilla/jss/crypto/PrivateKey.Type.html
- 6: https://www.dogtagpki.org/pki/javadocs/current/jss-4.2.6-24/org/mozilla/jss/crypto/PrivateKey.Type.html
- 7: https://www.dogtagpki.org/pki/javadocs/dogtag_10_3_5/jss/org/mozilla/jss/crypto/PrivateKey.Type.html
🏁 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:
JSS 5.10.0 PrivateKey.Type MLDSA toString() method implementation
💡 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:
- 1: https://dogtagpki.github.io/jss/master/javadocs/org/mozilla/jss/crypto/PrivateKey.Type.html
- 2: https://www.dogtagpki.org/pki/javadocs/dogtag_10_3_5/jss/org/mozilla/jss/crypto/PrivateKey.Type.html
- 3: https://phabricator.services.mozilla.com/D262394
- 4: https://searchfox.org/firefox-main/source/security/nss/lib/freebl/ml_dsa.c
- 5: https://dogtagpki.github.io/jss/master/javadocs/org/mozilla/jss/pkcs11/PK11PrivKey.html
- 6: https://downloads.bouncycastle.org/java/docs/bcprov-jdk18on-javadoc/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.html
- 7: https://downloads.bouncycastle.org/java/docs/bcprov-jdk15to18-javadoc/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.html
- 8: https://downloads.bouncycastle.org/java/docs/bcprov-jdk14-javadoc/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.html
- 9: https://downloads.bouncycastle.org/java/docs/bcprov-jdk15to18-javadoc/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.MLDSA.html
- 10: https://downloads.bouncycastle.org/java/docs/bcprov-jdk15to18-javadoc/org/bouncycastle/jcajce/provider/asymmetric/mldsa/HashSignatureSpi.MLDSA.html
- 11: https://javadoc.sic.tech/iaik_pq/current/iaik/security/pq/mldsa/MLDSA.html
🌐 Web query:
java.security.spec.NamedParameterSpec ML-DSA parameter names standard
💡 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:
- 1: https://openjdk.org/jeps/497
- 2: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/security/spec/NamedParameterSpec.html
- 3: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/security/spec/NamedParameterSpec.html
- 4: https://www.baeldung.com/java-quantum-resistant-ml-kem-ml-dsa
- 5: https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.204.pdf
- 6: https://bugs.openjdk.org/browse/JDK-8342282
- 7: https://docs.oracle.com/en/java/javase/24/docs/specs/security/standard-names.html
- 8: https://download.java.net/java/early_access/jdk27/docs/api/java.base/java/security/spec/NamedParameterSpec.html
- 9: https://bugs.openjdk.org/browse/JDK-8339010
NamedParameterSpec constructor will fail: Type.toString() doesn't match JDK standard ML-DSA names.
new NamedParameterSpec(pKey.getType().toString()) is incompatible with JDK's standard parameter names. The JDK standard names are "ML-DSA-44", "ML-DSA-65", "ML-DSA-87" (with hyphens), but if PrivateKey.Type enum values (MLDSA44, MLDSA65, MLDSA87) are added to JSS, their default toString() would return "MLDSA44" etc., causing NamedParameterSpec initialization to fail.
Use NamedParameterSpec.ML_DSA_44 (or equivalents) directly, or convert the Type enum value to the correct JDK format string:
Example fix
String 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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java` around lines
1486 - 1492, The NamedParameterSpec initialization in the MLDSA key generation
block is using pKey.getType().toString() which produces format like "MLDSA44",
but JDK expects the standard format "ML-DSA-44" with hyphens and uppercase DSA.
Replace the pKey.getType().toString() call with a switch statement that maps
Type.MLDSA44 to "ML-DSA-44", Type.MLDSA65 to "ML-DSA-65", and Type.MLDSA87 to
"ML-DSA-87", then pass the correctly formatted string to the NamedParameterSpec
constructor.
| 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"); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical
🧩 Analysis chain
🌐 Web query:
In Mozilla JSS, what does PK11PrivKey.getAlgorithm() return for an ML-DSA private key, and is PrivateKey.Type.toString() (e.g. for MLDSA65) a valid algorithm name accepted by java.security.Signature.getInstance()?
💡 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:
- 1: https://phabricator.services.mozilla.com/D262394
- 2: https://openjdk.org/jeps/497
- 3: https://bugs.openjdk.org/browse/JDK-8342282
- 4: https://bugs.openjdk.org/browse/JDK-8339010
🏁 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:
JSS Mozilla PK11PrivKey.Type.toString() ML-DSA return value format MLDSA65
💡 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:
- 1: https://phabricator.services.mozilla.com/D262394
- 2: https://phabricator.services.mozilla.com/D262393
- 3: https://dogtagpki.github.io/jss/master/javadocs/org/mozilla/jss/crypto/PrivateKey.Type.html
- 4: https://www.dogtagpki.org/pki/javadocs/current/jss-4.2.6-24/org/mozilla/jss/crypto/PrivateKey.Type.html
- 5: https://phabricator.services.mozilla.com/D262398
- 6: https://phabricator.services.mozilla.com/D263781
🌐 Web query:
JSS Java Security Services PrivateKey.Type toString ML-DSA what string
💡 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:
- 1: https://dogtagpki.github.io/jss/master/javadocs/org/mozilla/jss/crypto/PrivateKey.Type.html
- 2: https://www.dogtagpki.org/pki/javadocs/current/jss-4.2.6-24/org/mozilla/jss/crypto/PrivateKey.Type.html
- 3: https://openjdk.org/jeps/497
- 4: https://bugs.openjdk.org/browse/JDK-8342282
- 5: https://docs.oracle.com/en/java/javase/24/docs/specs/security/standard-names.html
The ML-DSA branch has a critical compatibility issue: JSS PrivateKey.Type does not currently support ML-DSA.
While priv.getAlgorithm() correctly returns "ML-DSA" for ML-DSA keys generated by JSS, the subsequent instanceof PK11PrivKey check followed by pKey.getType().toString() will fail or return an undefined value. Current JSS documentation shows that PrivateKey.Type is limited to RSA, DSA, and EC—ML-DSA is not defined as a type constant in the enum. This means getType() will not produce a valid Signature algorithm name (which require the format "ML-DSA-65", "ML-DSA-44", or "ML-DSA-87"), and will likely throw an exception or return null at runtime. Even if recent Mozilla NSS patches (D262394, D262393) add ML-DSA support, the JSS wrapper and its javadocs do not yet reflect this. This code path will fail unless your JSS version has been updated to match the underlying NSS changes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java` around lines
1521 - 1529, The ML-DSA case in the switch statement for handling private key
algorithms does not properly construct a valid Signature algorithm name because
JSS's PrivateKey.Type enum does not yet support ML-DSA constants. The call to
pKey.getType().toString() will not produce the required format (ML-DSA-65,
ML-DSA-44, or ML-DSA-87). Instead of relying on pKey.getType(), you should
directly construct the proper Signature algorithm string for ML-DSA by either
extracting the key parameter set (if determinable from the ML-DSA key itself) or
using a reasonable default like ML-DSA-65, then pass that string to
Signature.getInstance(). Alternatively, if you cannot reliably determine the
parameter set, provide a more informative error message indicating that ML-DSA
support requires a JSS version with proper ML-DSA type support.
edewata
left a comment
There was a problem hiding this comment.
So I have concerns about defaulting to hard-coded algs, but the code should work just fine. Feel free to merge as is if you prefer.



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:
Assisted-by: Claude Sonnet 4.5 noreply@anthropic.com
Summary by CodeRabbit
Release Notes