Skip to content

Commit 9b85b76

Browse files
committed
The DEFAULT and NONCEANDIV SecureRandom implementations now accept java.security.DrbgParameters on Java 9 and later, and DEFAULT is also registered as DRBG.
1 parent f9d452c commit 9b85b76

9 files changed

Lines changed: 614 additions & 15 deletions

File tree

‎core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ public String getAlgorithm()
5151
}
5252

5353
public void nextBytes(byte[] bytes)
54+
{
55+
nextBytes(bytes, (byte[])null);
56+
}
57+
58+
/**
59+
* Generate a user-specified number of random bytes, passing additional input to the DRBG.
60+
*
61+
* @param bytes the array to be filled in with random bytes.
62+
* @param additionalInput optional additional input for the DRBG, may be null (cast a literal null to byte[], as
63+
* Java 9 and later also have SecureRandom.nextBytes(byte[], SecureRandomParameters)).
64+
*/
65+
public void nextBytes(byte[] bytes, byte[] additionalInput)
5466
{
5567
synchronized (this)
5668
{
@@ -60,10 +72,10 @@ public void nextBytes(byte[] bytes)
6072
}
6173

6274
// check if a reseed is required...
63-
if (drbg.generate(bytes, null, predictionResistant) < 0)
75+
if (drbg.generate(bytes, additionalInput, predictionResistant) < 0)
6476
{
6577
drbg.reseed(null);
66-
drbg.generate(bytes, null, predictionResistant);
78+
drbg.generate(bytes, additionalInput, predictionResistant);
6779
}
6880
}
6981
}

‎docs/releasenotes.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ Date: 2026, TBD
106106

107107
- The provider adds ECDHwithSHA256HKDF, ECDHwithSHA384HKDF and ECDHwithSHA512HKDF KeyAgreement algorithms: ECDH followed by HKDF (RFC 5869), the counterparts for elliptic curve keys of the XDHwithSHA\*HKDF agreements used for the RFC 8418 dhSinglePass-stdDH-hkdf schemes. As with those, a UserKeyingMaterialSpec supplies the HKDF info and, optionally, its salt. They are registered by name only; the RFC 8418 scheme object identifiers remain registered to the XDH agreements.
108108

109+
- On Java 9 and later the provider's DEFAULT and NONCEANDIV SecureRandom implementations accept java.security.DrbgParameters: SecureRandom.getInstance() given a DrbgParameters.Instantiation creates a DRBG of its own with the requested security strength (rounded up to 112, 128, 192 or 256 bits, 256 being the default and the maximum), capability and personalization string, nextBytes() takes a DrbgParameters.NextBytes carrying additional input and a request for prediction resistance, reseed() takes a DrbgParameters.Reseed, and getParameters() reports how an instance was instantiated. An instance created without parameters uses the shared DRBG as before. DEFAULT is also registered under the alias DRBG, the name Java 9 gives an SP 800-90A DRBG, so with BC installed ahead of the SUN provider SecureRandom.getInstance("DRBG") returns BC's. Lightweight users gain SP800SecureRandom.nextBytes(byte[], byte[]) for passing additional input to the DRBG.
110+
109111
- TLS: Added CertificateStatusType.getName and getText, and TlsUtils.hasExtension.
110112

111113
### 2.1.4 Additional Notes

‎prov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java‎

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,18 @@ public Mappings()
118118

119119
public void configure(ConfigurableProvider provider)
120120
{
121-
provider.addAlgorithm("SecureRandom.DEFAULT", PREFIX + "$Default");
122-
provider.addAlgorithm("SecureRandom.NONCEANDIV", PREFIX + "$NonceAndIV");
121+
// the DRBGSpis jdk1.9 twin swaps in SPIs that also take java.security.DrbgParameters.
122+
provider.addAlgorithm("SecureRandom.DEFAULT", DRBGSpis.defaultSpi());
123+
provider.addAlgorithm("SecureRandom.NONCEANDIV", DRBGSpis.nonceAndIVSpi());
124+
// the name Java 9 and later give an SP 800-90A DRBG, which is what DEFAULT is.
125+
provider.addAlgorithm("Alg.Alias.SecureRandom.DRBG", "DEFAULT");
123126
}
124127
}
125128

126129
public static class Default
127130
extends SecureRandomSpi
128131
{
129-
private static final SecureRandom random = createBaseRandom(true);
132+
private static final SP800SecureRandom random = createBaseRandom(true);
130133

131134
public Default()
132135
{
@@ -151,7 +154,7 @@ protected byte[] engineGenerateSeed(int numBytes)
151154
public static class NonceAndIV
152155
extends SecureRandomSpi
153156
{
154-
private static final SecureRandom random = createBaseRandom(false);
157+
private static final SP800SecureRandom random = createBaseRandom(false);
155158

156159
public NonceAndIV()
157160
{
@@ -173,17 +176,47 @@ protected byte[] engineGenerateSeed(int numBytes)
173176
}
174177
}
175178

176-
private static SecureRandom createBaseRandom(boolean isPredictionResistant)
179+
/**
180+
* The DRBG shared by every DEFAULT SecureRandom created without parameters.
181+
*/
182+
static SP800SecureRandom getDefaultRandom()
183+
{
184+
return Default.random;
185+
}
186+
187+
/**
188+
* The DRBG shared by every NONCEANDIV SecureRandom created without parameters.
189+
*/
190+
static SP800SecureRandom getNonceAndIVRandom()
191+
{
192+
return NonceAndIV.random;
193+
}
194+
195+
private static SP800SecureRandom createBaseRandom(boolean isDefault)
196+
{
197+
return createBaseRandom(isDefault, isDefault, 256, null);
198+
}
199+
200+
/**
201+
* Create a DRBG from the provider's configured entropy source.
202+
*
203+
* @param isDefault true for a DEFAULT DRBG, false for a NONCEANDIV one (selects the generated personalization string).
204+
* @param isPredictionResistant true if every request should reseed.
205+
* @param securityStrength the security strength of the DRBG in bits, at most 256.
206+
* @param personalizationString the personalization string, or null to generate one.
207+
*/
208+
static SP800SecureRandom createBaseRandom(boolean isDefault, boolean isPredictionResistant, int securityStrength,
209+
byte[] personalizationString)
177210
{
178211
if (Properties.getPropertyValue(Properties.DRBG_ENTROPY_SOURCE) != null)
179212
{
180-
return createBaseRandom(isPredictionResistant, 128, createEntropySource());
213+
return createBaseRandom(isDefault, isPredictionResistant, securityStrength, personalizationString, 128, createEntropySource());
181214
}
182215
else if (Properties.isOverrideSet(Properties.DRBG_ENTROPY_THREAD))
183216
{
184217
initEntropyThread();
185218

186-
return createBaseRandom(isPredictionResistant, 256, new EntropySourceProvider()
219+
return createBaseRandom(isDefault, isPredictionResistant, securityStrength, personalizationString, 256, new EntropySourceProvider()
187220
{
188221
public EntropySource get(int bitsRequired)
189222
{
@@ -193,7 +226,7 @@ public EntropySource get(int bitsRequired)
193226
}
194227
else
195228
{
196-
return createBaseRandom(isPredictionResistant, 256, new EntropySourceProvider()
229+
return createBaseRandom(isDefault, isPredictionResistant, securityStrength, personalizationString, 256, new EntropySourceProvider()
197230
{
198231
public EntropySource get(int bitsRequired)
199232
{
@@ -203,15 +236,17 @@ public EntropySource get(int bitsRequired)
203236
}
204237
}
205238

206-
private static SecureRandom createBaseRandom(boolean isPredictionResistant, int entropyBits,
207-
EntropySourceProvider entropyProvider)
239+
private static SP800SecureRandom createBaseRandom(boolean isDefault, boolean isPredictionResistant,
240+
int securityStrength, byte[] personalizationString, int entropyBits, EntropySourceProvider entropyProvider)
208241
{
209242
EntropySource entropySource = entropyProvider.get(entropyBits);
210243

211-
byte[] personalisationString = generatePersonalizationString(isPredictionResistant, entropySource);
244+
byte[] personalisationString = (personalizationString != null)
245+
? personalizationString : generatePersonalizationString(isDefault, entropySource);
212246

213247
return new SP800SecureRandomBuilder(entropyProvider)
214248
.setPersonalizationString(personalisationString)
249+
.setSecurityStrength(securityStrength)
215250
.buildHash(new SHA512Digest(), entropySource.getEntropy(), isPredictionResistant);
216251
}
217252

@@ -328,10 +363,10 @@ private static byte[] generateNonceIVPersonalizationString(byte[] seed)
328363
Pack.longToLittleEndian(Thread.currentThread().getId()), Pack.longToLittleEndian(System.currentTimeMillis()));
329364
}
330365

331-
private static byte[] generatePersonalizationString(boolean isPredictionResistant, EntropySource entropySource)
366+
private static byte[] generatePersonalizationString(boolean isDefault, EntropySource entropySource)
332367
{
333368
byte[] entropy = entropySource.getEntropy();
334-
return isPredictionResistant
369+
return isDefault
335370
? generateDefaultPersonalizationString(entropy)
336371
: generateNonceIVPersonalizationString(entropy);
337372
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.bouncycastle.jcajce.provider.drbg;
2+
3+
/**
4+
* Multi-release hook naming the SecureRandom SPI classes DRBG.Mappings registers. This base copy
5+
* names the plain DRBG SPIs; the jdk1.9 twin names ParameterisedDRBG, whose SPIs also take
6+
* java.security.DrbgParameters. Keep the method set of the two copies identical. These are methods
7+
* rather than constants so the name is resolved against whichever copy the JDK loads.
8+
*/
9+
final class DRBGSpis
10+
{
11+
private DRBGSpis()
12+
{
13+
}
14+
15+
static String defaultSpi()
16+
{
17+
return DRBG.class.getName() + "$Default";
18+
}
19+
20+
static String nonceAndIVSpi()
21+
{
22+
return DRBG.class.getName() + "$NonceAndIV";
23+
}
24+
}

‎prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,17 @@ public Service run()
297297
{
298298
return null;
299299
}
300+
// swap each service once: one reached through an alias and then its own name (or the
301+
// reverse) must not be put again, which on JDK 11 fails for a SecureRandom service.
302+
String canonicalKey = service.getType() + "." + Strings.toUpperCase(service.getAlgorithm());
303+
Service swapped = serviceMap.get(canonicalKey);
304+
if (swapped != null)
305+
{
306+
serviceMap.put(key, swapped);
307+
return swapped;
308+
}
300309
serviceMap.put(key, service);
310+
serviceMap.put(canonicalKey, service);
301311
// remove legacy entry and swap to service entry
302312
BouncyCastleProvider.super.remove(service.getType() + "." + service.getAlgorithm());
303313
BouncyCastleProvider.super.putService(service);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.bouncycastle.jcajce.provider.drbg;
2+
3+
/**
4+
* Multi-release hook naming the SecureRandom SPI classes DRBG.Mappings registers. This jdk1.9 copy
5+
* names ParameterisedDRBG, whose SPIs also take java.security.DrbgParameters; the base copy names
6+
* the plain DRBG SPIs. Keep the method set of the two copies identical. These are methods rather
7+
* than constants so the name is resolved against whichever copy the JDK loads.
8+
*/
9+
final class DRBGSpis
10+
{
11+
private DRBGSpis()
12+
{
13+
}
14+
15+
static String defaultSpi()
16+
{
17+
return ParameterisedDRBG.class.getName() + "$Default";
18+
}
19+
20+
static String nonceAndIVSpi()
21+
{
22+
return ParameterisedDRBG.class.getName() + "$NonceAndIV";
23+
}
24+
}

0 commit comments

Comments
 (0)