Skip to content

Commit d57ff69

Browse files
committed
The BC ML-DSA, ML-KEM and SLH-DSA keys and the BCPQC keys with named parameter sets now implement getParams(), returning a NamedParameterSpec naming the parameter set on Java 11 and later as the JDK's own keys do, and the BCPQC MAYO, SDitH, SNOVA and NTRU+ KeyPairGenerators now accept a NamedParameterSpec, which they had refused whatever its case, relates to github #2467.
1 parent eeef018 commit d57ff69

55 files changed

Lines changed: 928 additions & 4 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/releasenotes.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Date: 2026, TBD
6565
- The ML-DSA public key constructor taking rho and t1 separately checked only for null, so an ML-DSA SubjectPublicKeyInfo in the SEQUENCE { OCTET STRING rho, OCTET STRING t1 } form with a t1 of the wrong length was accepted by PublicKeyFactory and the JCA KeyFactory, unlike the same key in the standard raw encoding. The component constructors for ML-DSA private keys (rho, K, tr, s1, s2, t0, t1, seed) and SLH-DSA private keys (skSeed, prf, pkSeed, pkRoot) likewise accepted components of any length. All three now reject a component whose length does not match the parameter set with an IllegalArgumentException.
6666
- The light-weight CryptoProWrapEngine (RFC 4357 sec. 6.3) diversified the key encryption key in the caller's own array, so after init the KeyParameter it was given held the diversified key, and initialising again with the same parameters - to unwrap what had just been wrapped, say - diversified it a second time and used a different key. It also failed with a NullPointerException when given no S-box, although init has a branch for that case. It now diversifies a copy, and given no S-box uses the GOST 28147 engine's default S-box for the diversification, the one the wrap itself then uses. The provider's GOST 28147 key wrap ciphers were unaffected, as they always supply an S-box and build a new KeyParameter on every init.
6767

68+
- The BCPQC MAYO, SDitH, SNOVA and NTRU+ KeyPairGenerators refused every java.security.spec.NamedParameterSpec with InvalidAlgorithmParameterException, whatever parameter set it named and in whatever case: they lower-cased the name and then looked it up in a table of upper-case names. The name is now upper-cased, so NamedParameterSpec("MAYO-1") and NamedParameterSpec("mayo-1") both select MAYO-1, as the other BCPQC families already allowed. The generators' own ParameterSpec classes were never affected.
69+
6870
### 2.1.3 Additional Features and Functionality
6971

7072
- The CRMF certificate request message controls now include the RFC 4211 sec. 6.6 protocolEncrKey control, which names the key a CA is to encrypt its response with: org.bouncycastle.cert.crmf.ProtocolEncrKeyControl carries the SubjectPublicKeyInfo the control is defined to take, and CertificateRequestMessage.getControl() recognises id-regCtrl-protocolEncrKey alongside the regToken, authenticator and pkiArchiveOptions controls it already returned (github PR #2443).
@@ -82,6 +84,8 @@ Date: 2026, TBD
8284

8385
- The BCJSSE provider adds an org.bouncycastle.jsse.BCSSLContext interface exposing extended functionality of its SSLContext, obtained with org.bouncycastle.jsse.util.ContextUtil.getBCSSLContext() by way of the new BCSSLSessionContext interface the context's session contexts implement. Its getDefaultParameters(boolean) and getSupportedParameters(boolean) return the context's default and supported parameters as a BCSSLParameters for either client or server mode, including the BC-specific properties, where SSLContext.getDefaultSSLParameters() and getSupportedSSLParameters() report client mode only and cannot carry those properties. A BCSSLContext describes the initialization of the SSLContext it was obtained from, and is not updated if the SSLContext is re-initialized.
8486

87+
- The BC provider's ML-DSA, ML-KEM and SLH-DSA keys now implement getParams(), which keys inherit from java.security.AsymmetricKey from Java 22, returning a java.security.spec.NamedParameterSpec naming the parameter set (for example "ML-DSA-65") on Java 11 and later, and null before. The JDK's own ML-DSA and ML-KEM keys return only the family name, "ML-DSA" or "ML-KEM", from getAlgorithm() and report the parameter set this way, so comparing getParams() rather than getAlgorithm() now identifies the parameter set whichever provider made the key. The BCPQC provider's keys do the same for every family with named parameter sets - AIMer, BIKE, FAEST, Falcon, HAETAE, HQC, MAYO, MQOM, NTRU, NTRU+, NTRU LPRime, Streamlined NTRU Prime, QR-UOV, SABER, SDitH, SMAUG-T, SNOVA, SQIsign and UOV - with the name their ParameterSpec reports, which the family's KeyPairGenerator accepts back. getAlgorithm() on BC and BCPQC keys is unchanged (github #2467).
88+
8589
### 2.1.4 Additional Notes
8690

8791
- The sources and javadoc jars of the Ant-built distributions (jdk14, jdk15to18 and jdk13) no longer carry test material. Each module's javadoc target copies the package documentation it needs - org/bouncycastle/<area>/**/*.html - back into the module source directory that has already been compiled from, and zip-src zips that directory afterwards, so every test package's package.html arrived in the sources jar by that route; javadoc-util additionally copied org/bouncycastle/asn1/isismtt/**/*.java, which put test classes into the bcutil javadoc as generated pages, and javadoc-pg deliberately copied the gpg and bcpg test sources in order to document them. Separately the source copies excluded test material only one directory deep and only for *.java, because Ant reads ** as an any-depth wildcard just where it is a whole path segment, so anything nested further or with another extension - the PEM certificate fixtures under org/bouncycastle/est/test/san corrected in 1.86, and an ICAO master list under org/bouncycastle/asn1/icao/test - went through. The source and javadoc copies of every module now exclude test directories at any depth, and javadoc-pg no longer documents the test packages. org.bouncycastle.util.test is unaffected and still ships in the bcprov binary, sources and javadoc jars, as it does from the Gradle build: it is the SimpleTest framework the light-weight API's own test classes are written against, not test material of the distribution. No binary changes - the classes and resources of every Ant-built jar are identical to those of the 1.86 release - and the Gradle-built jdk18on artifacts never carried any of this.

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.IOException;
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
6+
import java.security.spec.AlgorithmParameterSpec;
67

78
import javax.security.auth.Destroyable;
89

@@ -15,6 +16,7 @@
1516
import org.bouncycastle.jcajce.interfaces.MLDSAPrivateKey;
1617
import org.bouncycastle.jcajce.interfaces.MLDSAPublicKey;
1718
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
19+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1820
import org.bouncycastle.jcajce.spec.MLDSAParameterSpec;
1921
import org.bouncycastle.util.Arrays;
2022
import org.bouncycastle.util.Exceptions;
@@ -160,6 +162,17 @@ public byte[] getSeed()
160162
return params.getSeed();
161163
}
162164

165+
/**
166+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
167+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
168+
*
169+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
170+
*/
171+
public AlgorithmParameterSpec getParams()
172+
{
173+
return NamedParameterSpecUtil.getNamedParameterSpec(getAlgorithm());
174+
}
175+
163176
public MLDSAParameterSpec getParameterSpec()
164177
{
165178
return MLDSAParameterSpec.fromName(params.getParameters().getName());

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPublicKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import java.io.IOException;
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
6+
import java.security.spec.AlgorithmParameterSpec;
67

78
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
89
import org.bouncycastle.crypto.params.MLDSAPublicKeyParameters;
910
import org.bouncycastle.crypto.util.PublicKeyFactory;
1011
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
1112
import org.bouncycastle.jcajce.interfaces.BCKey;
1213
import org.bouncycastle.jcajce.interfaces.MLDSAPublicKey;
14+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1315
import org.bouncycastle.jcajce.spec.MLDSAParameterSpec;
1416
import org.bouncycastle.util.Arrays;
1517
import org.bouncycastle.util.Fingerprint;
@@ -104,6 +106,17 @@ public String getFormat()
104106
return "X.509";
105107
}
106108

109+
/**
110+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
111+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
112+
*
113+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
114+
*/
115+
public AlgorithmParameterSpec getParams()
116+
{
117+
return NamedParameterSpecUtil.getNamedParameterSpec(getAlgorithm());
118+
}
119+
107120
public MLDSAParameterSpec getParameterSpec()
108121
{
109122
return MLDSAParameterSpec.fromName(params.getParameters().getName());

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.IOException;
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
6+
import java.security.spec.AlgorithmParameterSpec;
67

78
import javax.security.auth.Destroyable;
89

@@ -14,6 +15,7 @@
1415
import org.bouncycastle.jcajce.interfaces.BCKey;
1516
import org.bouncycastle.jcajce.interfaces.MLKEMPrivateKey;
1617
import org.bouncycastle.jcajce.interfaces.MLKEMPublicKey;
18+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1719
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
1820
import org.bouncycastle.util.Arrays;
1921
import org.bouncycastle.util.Exceptions;
@@ -150,6 +152,17 @@ public MLKEMPrivateKey getPrivateKey(boolean preferSeedOnly)
150152
return new BCMLKEMPrivateKey(this.params.withPreferredFormat(MLKEMPrivateKeyParameters.EXPANDED_KEY));
151153
}
152154

155+
/**
156+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
157+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
158+
*
159+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
160+
*/
161+
public AlgorithmParameterSpec getParams()
162+
{
163+
return NamedParameterSpecUtil.getNamedParameterSpec(getAlgorithm());
164+
}
165+
153166
public MLKEMParameterSpec getParameterSpec()
154167
{
155168
return MLKEMParameterSpec.fromName(params.getParameters().getName());

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPublicKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import java.io.IOException;
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
6+
import java.security.spec.AlgorithmParameterSpec;
67

78
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
89
import org.bouncycastle.crypto.params.MLKEMPublicKeyParameters;
910
import org.bouncycastle.crypto.util.PublicKeyFactory;
1011
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
1112
import org.bouncycastle.jcajce.interfaces.BCKey;
1213
import org.bouncycastle.jcajce.interfaces.MLKEMPublicKey;
14+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1315
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
1416
import org.bouncycastle.util.Arrays;
1517
import org.bouncycastle.util.Fingerprint;
@@ -109,6 +111,17 @@ public String getFormat()
109111
return "X.509";
110112
}
111113

114+
/**
115+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
116+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
117+
*
118+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
119+
*/
120+
public AlgorithmParameterSpec getParams()
121+
{
122+
return NamedParameterSpecUtil.getNamedParameterSpec(getAlgorithm());
123+
}
124+
112125
public MLKEMParameterSpec getParameterSpec()
113126
{
114127
return MLKEMParameterSpec.fromName(params.getParameters().getName());

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/slhdsa/BCSLHDSAPrivateKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.IOException;
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
6+
import java.security.spec.AlgorithmParameterSpec;
67

78
import javax.security.auth.Destroyable;
89

@@ -15,6 +16,7 @@
1516
import org.bouncycastle.jcajce.interfaces.BCKey;
1617
import org.bouncycastle.jcajce.interfaces.SLHDSAPrivateKey;
1718
import org.bouncycastle.jcajce.interfaces.SLHDSAPublicKey;
19+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1820
import org.bouncycastle.jcajce.spec.SLHDSAParameterSpec;
1921
import org.bouncycastle.util.Arrays;
2022
import org.bouncycastle.util.Exceptions;
@@ -115,6 +117,17 @@ public SLHDSAPublicKey getPublicKey()
115117
return new BCSLHDSAPublicKey(new SLHDSAPublicKeyParameters(params.getParameters(), params.getPublicKey()));
116118
}
117119

120+
/**
121+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
122+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
123+
*
124+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
125+
*/
126+
public AlgorithmParameterSpec getParams()
127+
{
128+
return NamedParameterSpecUtil.getNamedParameterSpec(getAlgorithm());
129+
}
130+
118131
public SLHDSAParameterSpec getParameterSpec()
119132
{
120133
return SLHDSAParameterSpec.fromName(params.getParameters().getName());

‎prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/slhdsa/BCSLHDSAPublicKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import java.io.IOException;
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
6+
import java.security.spec.AlgorithmParameterSpec;
67

78
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
89
import org.bouncycastle.crypto.params.SLHDSAPublicKeyParameters;
910
import org.bouncycastle.crypto.util.PublicKeyFactory;
1011
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
1112
import org.bouncycastle.jcajce.interfaces.BCKey;
1213
import org.bouncycastle.jcajce.interfaces.SLHDSAPublicKey;
14+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1315
import org.bouncycastle.jcajce.spec.SLHDSAParameterSpec;
1416
import org.bouncycastle.util.Arrays;
1517
import org.bouncycastle.util.Fingerprint;
@@ -101,6 +103,17 @@ public String getFormat()
101103
return "X.509";
102104
}
103105

106+
/**
107+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
108+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
109+
*
110+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
111+
*/
112+
public AlgorithmParameterSpec getParams()
113+
{
114+
return NamedParameterSpecUtil.getNamedParameterSpec(getAlgorithm());
115+
}
116+
104117
public SLHDSAParameterSpec getParameterSpec()
105118
{
106119
return SLHDSAParameterSpec.fromName(params.getParameters().getName());
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.bouncycastle.jcajce.provider.asymmetric.util;
2+
3+
import java.security.spec.AlgorithmParameterSpec;
4+
5+
/**
6+
* Version hook producing the JDK form of a key's named parameter set, as returned by the
7+
* getParams() method keys inherit from java.security.AsymmetricKey from Java 22.
8+
* <p>
9+
* java.security.spec.NamedParameterSpec only exists from Java 11, so this copy, loaded on earlier
10+
* JVMs, has nothing to offer and returns null. The jdk1.11 twin returns a NamedParameterSpec - keep
11+
* the method set of the two copies identical, and put nothing else on this class.
12+
* </p>
13+
*/
14+
public class NamedParameterSpecUtil
15+
{
16+
/**
17+
* Return a NamedParameterSpec for the passed in parameter set name, if the JVM has one.
18+
*
19+
* @param name the standard name of the parameter set, for example "ML-DSA-65".
20+
* @return null, as NamedParameterSpec is not available before Java 11.
21+
*/
22+
public static AlgorithmParameterSpec getNamedParameterSpec(String name)
23+
{
24+
return null;
25+
}
26+
}

‎prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/aimer/BCAIMerPrivateKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
66
import java.security.PrivateKey;
7+
import java.security.spec.AlgorithmParameterSpec;
78

89
import org.bouncycastle.asn1.ASN1Set;
910
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
11+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
1012
import org.bouncycastle.pqc.crypto.aimer.AIMerPrivateKeyParameters;
1113
import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory;
1214
import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory;
@@ -93,6 +95,17 @@ public byte[] getEncoded()
9395
}
9496
}
9597

98+
/**
99+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
100+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
101+
*
102+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
103+
*/
104+
public AlgorithmParameterSpec getParams()
105+
{
106+
return NamedParameterSpecUtil.getNamedParameterSpec(getParameterSpec().getName());
107+
}
108+
96109
public AIMerParameterSpec getParameterSpec()
97110
{
98111
return AIMerParameterSpec.fromName(params.getParameters().getName());

‎prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/aimer/BCAIMerPublicKey.java‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import java.io.ObjectInputStream;
55
import java.io.ObjectOutputStream;
66
import java.security.PublicKey;
7+
import java.security.spec.AlgorithmParameterSpec;
78

89
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
10+
import org.bouncycastle.jcajce.provider.asymmetric.util.NamedParameterSpecUtil;
911
import org.bouncycastle.pqc.crypto.aimer.AIMerPublicKeyParameters;
1012
import org.bouncycastle.pqc.crypto.util.PublicKeyFactory;
1113
import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory;
@@ -94,6 +96,17 @@ public String getFormat()
9496
return "X.509";
9597
}
9698

99+
/**
100+
* Return the parameter set of this key as a java.security.spec.NamedParameterSpec, the form the
101+
* JDK's own keys report through java.security.AsymmetricKey.getParams() from Java 22.
102+
*
103+
* @return a NamedParameterSpec naming the parameter set on Java 11 and later, null otherwise.
104+
*/
105+
public AlgorithmParameterSpec getParams()
106+
{
107+
return NamedParameterSpecUtil.getNamedParameterSpec(getParameterSpec().getName());
108+
}
109+
97110
public AIMerParameterSpec getParameterSpec()
98111
{
99112
return AIMerParameterSpec.fromName(params.getParameters().getName());

0 commit comments

Comments
 (0)