Skip to content

Use parent CA key type for LWCA generation - #5393

Merged
fmarco76 merged 1 commit into
dogtagpki:masterfrom
fmarco76:LWCA
Jun 24, 2026
Merged

Use parent CA key type for LWCA generation#5393
fmarco76 merged 1 commit into
dogtagpki:masterfrom
fmarco76:LWCA

Conversation

@fmarco76

@fmarco76 fmarco76 commented Jun 19, 2026

Copy link
Copy Markdown
Member

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

Summary by CodeRabbit

Release Notes

  • New Features
    • Enhanced certificate authority key generation to support EC and ML-DSA private key types.
    • Automatically selects the appropriate signing algorithm based on the private key type when creating certificate requests.
  • Configuration
    • Expanded the accepted certificate key parameter set to include additional entries (now supports ML-DSA key parameter values).

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

CertificateAuthority.generateKeyPair is reworked to branch on the signing unit's private key type (RSA, EC, ML-DSA via PK11PrivKey, or fallback RSA). generateCertRequest now selects the Signature algorithm dynamically based on the keypair's private key algorithm. The caCACert.cfg profile's keyParameters list is expanded to accept the additional key types.

Changes

Multi-algorithm CA cert support

Layer / File(s) Summary
caCACert profile key parameter expansion
base/ca/shared/profiles/ca/caCACert.cfg
Expands policyset.caCertSet.3.constraint.params.keyParameters to include additional key sizes and types (44,65,87).
Multi-algorithm key-pair generation and cert request signing
base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
Adds imports for NamedParameterSpec, Signature, PrivateKey.Type, and PK11PrivKey; rewrites generateKeyPair to branch by PK11PrivKey type (RSA/EC by key strength, MLDSA via NamedParameterSpec, fallback RSA from modulus bit length or 3072); updates generateCertRequest to select SHA256withRSA, SHA256withECDSA, or the ML-DSA algorithm string based on the private key's algorithm, throwing ECAException for unhandled ML-DSA cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • dogtagpki/pki#5351: Both PRs implement PQC (ML-DSA/ML-KEM) enrollment flows—this PR updates CertificateAuthority.generateKeyPair/generateCertRequest to create and sign with the correct PQC key/signature algorithms, while the related PR updates CRMFPopClient and KeyConstraint to generate and handle PQC CRMF requests.

Suggested reviewers

  • ladycfu

Poem

🐇 Hop, hop, the keys expand today,
RSA, EC, and ML-DSA join the fray!
No more hardcoded SHA256withRSA alone—
Each key type now claims its algorithmic throne.
The profile config widens its welcoming gate,
Post-quantum and classic keys: all celebrated! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Use parent CA key type for LWCA generation' directly and accurately reflects the main objective: enabling LWCA key generation to match the parent CA's key type instead of defaulting to RSA.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
Comment thread base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
Comment thread base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 JSS KeyPairGenerator for 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 calls keygen.getCurveCodeByName(curveName) before initializing. Extract the curve information from the parent EC key (cast pKey to PK11ECPrivateKey and call getParams()) 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

📥 Commits

Reviewing files that changed from the base of the PR and between 544da29 and 79dbfe2.

📒 Files selected for processing (2)
  • base/ca/shared/profiles/ca/caCACert.cfg
  • base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java

Comment thread base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
@sonarqubecloud

Copy link
Copy Markdown

@edewata edewata left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my comments below.

Comment on lines +1473 to +1475
} catch (Exception e) {
logger.warn("CertificateAuthority: failed to get EC curve name from parent certificate: " + e.getMessage());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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.

Comment thread base/ca/src/main/java/com/netscape/ca/CertificateAuthority.java
Comment on lines 1500 to 1503
int keySize = 3072;
PublicKey thisPub = mSigningUnit.getPublicKey();
if (thisPub instanceof RSAKey) {
keySize = ((RSAKey) thisPub).getModulus().bitLength();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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(new NamedParameterSpec(pKey.getType().toString()));
return gen.genKeyPair();
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 thisPriv instanceof PK11PrivKey case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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.

Comment on lines +1528 to +1529
logger.warn("Private key type not recognised. Try with SHA256withRSA signature");
yield Signature.getInstance("SHA256withRSA");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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.

Comment on lines +1519 to +1520
case "RSA" -> Signature.getInstance("SHA256withRSA");
case "EC" -> Signature.getInstance("SHA256withECDSA");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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?

Comment thread base/ca/shared/profiles/ca/caCACert.cfg
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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between fb64114 and da1390e.

📒 Files selected for processing (2)
  • base/ca/shared/profiles/ca/caCACert.cfg
  • base/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

Comment on lines +1486 to +1492
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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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:


🏁 Script executed:

# Search for JSS PrivateKey.Type enum definition
rg -t java "enum\s+Type|class\s+Type" --search-path . -A 20 | head -100

Repository: 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 5

Repository: 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:


🏁 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.java

Repository: dogtagpki/pki

Length of output: 1111


🏁 Script executed:

# Look for build files to identify JSS version
fd -t f "pom.xml|build.gradle" | head -5

Repository: 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 -10

Repository: 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 -20

Repository: dogtagpki/pki

Length of output: 151


🏁 Script executed:

# Search for NamedParameterSpec usage patterns
rg -t java "NamedParameterSpec" -B 3 -A 3

Repository: 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 5

Repository: dogtagpki/pki

Length of output: 151


🏁 Script executed:

# Check pom.xml for JSS dependency
cat base/ca/pom.xml

Repository: 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 -50

Repository: 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 -100

Repository: dogtagpki/pki

Length of output: 151


🏁 Script executed:

# Check root pom.xml
cat pom.xml | grep -A 5 -B 5 "jss\|JSS" | head -50

Repository: dogtagpki/pki

Length of output: 151


🏁 Script executed:

# List the directory structure to understand the project layout
ls -la base/ | head -20

Repository: 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.xml

Repository: dogtagpki/pki

Length of output: 1209


🏁 Script executed:

# Look for CMakeLists.txt which might define JSS version
grep -i "jss" base/CMakeLists.txt | head -20

Repository: 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 -30

Repository: 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 3

Repository: 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 -10

Repository: 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:


🌐 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:


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.

Comment on lines +1521 to +1529
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");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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:


🏁 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 -20

Repository: 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:


🌐 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:


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 edewata left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@fmarco76

Copy link
Copy Markdown
Member Author

@edewata thanks. I am merging this code but I have created a new #5394 as follow up. I'll add the option for the algorithm during creation and remove the default so if the parent is not accessible then the exception can be thrown.

@fmarco76
fmarco76 merged commit f377ce5 into dogtagpki:master Jun 24, 2026
292 of 308 checks passed
@fmarco76
fmarco76 deleted the LWCA branch June 24, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants