Details
-
Bug
-
Resolution: Fixed
-
P4
-
11, 16, 17
-
b15
-
generic
-
generic
-
Not verified
Backports
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8310124 | 11.0.21 | Lucy Schmidt | P4 | Resolved | Fixed | b01 |
JDK-8303103 | 11.0.20-oracle | Prasadarao Koppula | P4 | Resolved | Fixed | b01 |
JDK-8324615 | 8u381 | Prasadarao Koppula | P4 | Resolved | Fixed | b01 |
Description
A DESCRIPTION OF THE PROBLEM :
The Javadoc for Cipher.init(int opmode, Key key) says the following:
"If this cipher (including its underlying feedback or padding scheme) requires any random bytes (e.g., for parameter generation), it will get them using the SecureRandom implementation of the highest-priority installed provider as the source of randomness."
However the implementation of this method does this (I checked Oracle JDK v1.8 and Zulu JDK 11.0.9 based on OpenJDK):
public final void init(int opmode, Key key) throws InvalidKeyException {
init(opmode, key, JceSecurity.RANDOM);
}
And this is JceSecurity.RANDOM:
static final SecureRandom RANDOM = new SecureRandom();
As a result, it doesn't matter what the highest priority provider is at the moment this method is called. It will always pick a previously created instance that is associated with whatever provider was highest priority at the time that it was created.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I tested this using the following two Bouncy Castle providers but it can happen with any Provider:
* https://mvnrepository.com/artifact/org.bouncycastle/bc-fips
* https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
In the source code for a test case I'll use the former.
Steps:
1. Instantiate a Cipher object and initialize it using Cipher.init(int, key).
2. Add a new provider at highest priority, one that supports SecureRandom.
3. Instantiate a Cipher object and initialize it using Cipher.init(int, key).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The call in step 3 should create a SecureRandom instance that uses the new provider.
The implementation should be the following or (if the intent is to cache the instance) it should check if the an instance is cached for the current highest priority provider of SecureRandom:
public final void init(int opmode, Key key) throws InvalidKeyException {
init(opmode, key, new SecureRandom());
}
ACTUAL -
The new provider is not used. If you use the FIPS-compliant BC Provider in strict mode it causes an exception, which is a good way to test this at runtime. See the source code for an executable test case.
---------- BEGIN SOURCE ----------
void testCipherInitSecureRandomBug() throws Exception {
CryptoServicesRegistrar.setApprovedOnlyMode(true);
BouncyCastleFipsProvider bcProvider = new BouncyCastleFipsProvider();
// ensure that the highest priority provider for SecureRandom is SUN
System.out.println(new SecureRandom().getProvider().getName());
assert(new SecureRandom().getProvider().getName().equals("SUN"));
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[32], "AES")); // succeeds
Security.insertProviderAt(bcProvider, 1);
// ensure that the highest priority provider for SecureRandom is BC
assert(new SecureRandom().getProvider().getName().startsWith("BC"));
cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[32], "AES")); // fails
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Call an init() method that accepts an explicit SecureRandom argument.
Unfortunately this workaround may not work for applications relying on libraries that make the Cipher.init() call.
FREQUENCY : always
The Javadoc for Cipher.init(int opmode, Key key) says the following:
"If this cipher (including its underlying feedback or padding scheme) requires any random bytes (e.g., for parameter generation), it will get them using the SecureRandom implementation of the highest-priority installed provider as the source of randomness."
However the implementation of this method does this (I checked Oracle JDK v1.8 and Zulu JDK 11.0.9 based on OpenJDK):
public final void init(int opmode, Key key) throws InvalidKeyException {
init(opmode, key, JceSecurity.RANDOM);
}
And this is JceSecurity.RANDOM:
static final SecureRandom RANDOM = new SecureRandom();
As a result, it doesn't matter what the highest priority provider is at the moment this method is called. It will always pick a previously created instance that is associated with whatever provider was highest priority at the time that it was created.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I tested this using the following two Bouncy Castle providers but it can happen with any Provider:
* https://mvnrepository.com/artifact/org.bouncycastle/bc-fips
* https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
In the source code for a test case I'll use the former.
Steps:
1. Instantiate a Cipher object and initialize it using Cipher.init(int, key).
2. Add a new provider at highest priority, one that supports SecureRandom.
3. Instantiate a Cipher object and initialize it using Cipher.init(int, key).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The call in step 3 should create a SecureRandom instance that uses the new provider.
The implementation should be the following or (if the intent is to cache the instance) it should check if the an instance is cached for the current highest priority provider of SecureRandom:
public final void init(int opmode, Key key) throws InvalidKeyException {
init(opmode, key, new SecureRandom());
}
ACTUAL -
The new provider is not used. If you use the FIPS-compliant BC Provider in strict mode it causes an exception, which is a good way to test this at runtime. See the source code for an executable test case.
---------- BEGIN SOURCE ----------
void testCipherInitSecureRandomBug() throws Exception {
CryptoServicesRegistrar.setApprovedOnlyMode(true);
BouncyCastleFipsProvider bcProvider = new BouncyCastleFipsProvider();
// ensure that the highest priority provider for SecureRandom is SUN
System.out.println(new SecureRandom().getProvider().getName());
assert(new SecureRandom().getProvider().getName().equals("SUN"));
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[32], "AES")); // succeeds
Security.insertProviderAt(bcProvider, 1);
// ensure that the highest priority provider for SecureRandom is BC
assert(new SecureRandom().getProvider().getName().startsWith("BC"));
cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[32], "AES")); // fails
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Call an init() method that accepts an explicit SecureRandom argument.
Unfortunately this workaround may not work for applications relying on libraries that make the Cipher.init() call.
FREQUENCY : always
Attachments
Issue Links
- backported by
-
JDK-8303103 Cipher.init(int, key) does not use highest priority provider for random bytes
- Resolved
-
JDK-8310124 Cipher.init(int, key) does not use highest priority provider for random bytes
- Resolved
-
JDK-8324615 Cipher.init(int, key) does not use highest priority provider for random bytes
- Resolved
- links to
-
Commit openjdk/jdk11u-dev/c1939dd3
-
Commit openjdk/jdk/434a399b
-
Review openjdk/jdk11u-dev/1902
-
Review openjdk/jdk/3018
(2 links to)