# 13
I'd like to jump in here and say I've been affected by this issue also, but I think the issue is more sinister than it first appears.
I'm in the process of upgrading our servers from JDK 5 to JDK 6. We have been using PBEWITHMD5ANDTRIPLEDES to encrypt our data, and we have not needed to install the Unlimited Strength Java(TM) Cryptography Extension Policy Files to date - PBEWITHMD5ANDTRIPLEDES seemed to work out of the box.
When upgrading to JDK 6 however, I started seeing this "Illegal key size" exception. Installing the policy files fixes the exception - but my servers running JDK 5 are no longer able to pass encrypted data to servers running JDK 6.
I ran the following test case under JDK 5 and JDK 6 with and without the policy files installed, on both Linux and Windows. It looks to me like JDK 5 does not use the encryption algorithm you specify at all -- and defaults to PBEWITHMD5ANDDES - i.e. 56-bit security. Is this a glaring security flaw in JDK 5, which is actually fixed in JDK 6?
Here's the test code:
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.Cipher;
import java.security.spec.KeySpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.Security;
import java.util.Arrays;
public class JDKIssueTestCase {
public static void main(String[] args) {
System.out.println("JVM Name: \"" + System.getProperty("java.vm.name") + "\" Version: \"" + System.getProperty("java.vm.version") + "\" Vendor: \"" + System.getProperty("java.vm.vendor") + "\"");
System.out.println("OS Name: \"" + System.getProperty("os.name") + "\" Version: \"" + System.getProperty("os.version") + "\" Architecture: \"" + System.getProperty("os.arch") + "\"");
System.out.println("Sun JCE: [" + Security.getProvider("SunJCE") + "] Info: [" + Security.getProvider("SunJCE").getInfo() + "]");
String[] algorithmsToTest = { "PBEWITHMD5ANDTRIPLEDES", "PBEWITHSHA1ANDDESEDE", "PBEWITHMD5ANDDES", "PBEWITHSHA1ANDRC2_40" };
for (String algorithm : algorithmsToTest) {
String algorithmMaxAllowedKeyLength;
try {
algorithmMaxAllowedKeyLength = String.valueOf(Cipher.getMaxAllowedKeyLength(algorithm));
}
catch (Exception e) {
algorithmMaxAllowedKeyLength = e.toString();
}
String result;
try {
// Text to encrypt...
final String testInputText = "This is a test.";
final String testPassphrase = "Test passphrase.";
// Random salt...
final byte[] salt = {
(byte)0xA5, (byte)0x9E, (byte)0xC8, (byte)0x32,
(byte)0x43, (byte)0xF5, (byte)0xEA, (byte)0x03
};
// Create the encryption key from the passphrase...
KeySpec keySpec = new PBEKeySpec(testPassphrase.toCharArray(), salt, 19);
SecretKey key = SecretKeyFactory.getInstance(algorithm).generateSecret(keySpec);
// Prepare the ciphers from the key...
Cipher ecipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the salt parameter to the ciphers...
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, 19);
// Initialise the cipher...
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
// Get input as bytes...
byte[] utf8 = testInputText.getBytes("UTF8");
// Encrypt the bytes...
byte[] enc = ecipher.doFinal(utf8);
result = "Encrypted: " + Arrays.toString(enc);
}
catch (Exception e) {
result = "Failed: [" + e.toString() + "]";
}
System.out.println("\nAlgorithm: [" + algorithm + "] Max key length: [" + algorithmMaxAllowedKeyLength + "]\nResult " + result);
}
}
}
And here's the output under various environments:
=========================
JDK 1.5 no policy files installed
JVM Name: "Java HotSpot(TM) 64-Bit Server VM" Version: "1.5.0_09-b03" Vendor: "Sun Microsystems Inc."
OS Name: "Linux" Version: "2.6.19-1.2895.fc6" Architecture: "amd64"
Sun JCE: [SunJCE version 1.5] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
Algorithm: [PBEWITHMD5ANDTRIPLEDES] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDDESEDE] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHMD5ANDDES] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDRC2_40] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
=========================
JDK 1.5 with policy files installed
JVM Name: "Java HotSpot(TM) 64-Bit Server VM" Version: "1.5.0_09-b03" Vendor: "Sun Microsystems Inc."
OS Name: "Linux" Version: "2.6.19-1.2895.fc6" Architecture: "amd64"
Sun JCE: [SunJCE version 1.5] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
Algorithm: [PBEWITHMD5ANDTRIPLEDES] Max key length: [2147483647]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDDESEDE] Max key length: [2147483647]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHMD5ANDDES] Max key length: [2147483647]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDRC2_40] Max key length: [2147483647]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
=========================
JDK 1.6 no policy files installed
JVM Name: "Java HotSpot(TM) 64-Bit Server VM" Version: "1.6.0-b105" Vendor: "Sun Microsystems Inc."
OS Name: "Linux" Version: "2.6.19-1.2895.fc6" Architecture: "amd64"
Sun JCE: [SunJCE version 1.6] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
Algorithm: [PBEWITHMD5ANDTRIPLEDES] Max key length: [128]
Result Failed: [java.security.InvalidKeyException: Illegal key size]
Algorithm: [PBEWITHSHA1ANDDESEDE] Max key length: [128]
Result Encrypted: [-70, -45, 57, 35, -29, 5, -113, -118, 102, 14, -119, -81, 125, 8, 56, -8]
Algorithm: [PBEWITHMD5ANDDES] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDRC2_40] Max key length: [128]
Result Encrypted: [50, 66, -63, -84, -123, 13, -113, 77, 67, 80, -66, -70, 90, 23, -53, 13]
=========================
JDK 1.6 with policy files installed
JVM Name: "Java HotSpot(TM) 64-Bit Server VM" Version: "1.6.0-b105" Vendor: "Sun Microsystems Inc."
OS Name: "Linux" Version: "2.6.19-1.2895.fc6" Architecture: "amd64"
Sun JCE: [SunJCE version 1.6] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
Algorithm: [PBEWITHMD5ANDTRIPLEDES] Max key length: [2147483647]
Result Encrypted: [120, 127, -52, -65, -121, 88, 20, 42, 27, -61, -101, 69, -82, -18, 127, 28]
Algorithm: [PBEWITHSHA1ANDDESEDE] Max key length: [2147483647]
Result Encrypted: [-70, -45, 57, 35, -29, 5, -113, -118, 102, 14, -119, -81, 125, 8, 56, -8]
Algorithm: [PBEWITHMD5ANDDES] Max key length: [2147483647]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDRC2_40] Max key length: [2147483647]
Result Encrypted: [50, 66, -63, -84, -123, 13, -113, 77, 67, 80, -66, -70, 90, 23, -53, 13]
=========================
Additional test: JDK 1.5 no policy files installed on Windows
JVM Name: "Java HotSpot(TM) Client VM" Version: "1.5.0-b64" Vendor: "Sun Microsystems Inc."
OS Name: "Windows XP" Version: "5.1" Architecture: "x86"
Sun JCE: [SunJCE version 1.5] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
Algorithm: [PBEWITHMD5ANDTRIPLEDES] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDDESEDE] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHMD5ANDDES] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Algorithm: [PBEWITHSHA1ANDRC2_40] Max key length: [128]
Result Encrypted: [-56, -33, -40, -75, -41, 48, -39, 83, 122, 88, -70, 27, -69, 55, 85, -18]
Note the output of all 4 algorithms under JDK 5 is the same. It matches the output of the PBEWITHMD5ANDDES algorithm under JDK 6.
If this really is a bug, it is a whopping big bug. It would mean that everyone out there using PBE encryption under JDK 5 is getting only 56-bit DES encryption no matter what algorithms they think they're using.
Please tell me if I'm doing something wrong here, and this not actually a bug!
Can anybody run this test to verify?
# 16
I just investigated further: there is a bug in Key.getAlgorithm() in JDK 5. No matter what algorithm the key was generated for, this method returns PBEWITHMD5ANDDES.
I think this proves the bug exists, and has serious security implications for users of PBE encryption on JDK5 (and possibly earlier versions).
Here is a test case:
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import java.security.spec.KeySpec;
import java.security.Security;
public class JDKIssueBugIdentifiedTestCase {
public static void main(String[] args) {
System.out.println("JVM Name: \"" + System.getProperty("java.vm.name") + "\" Version: \"" + System.getProperty("java.vm.version") + "\" Vendor: \"" + System.getProperty("java.vm.vendor") + "\"");
System.out.println("OS Name: \"" + System.getProperty("os.name") + "\" Version: \"" + System.getProperty("os.version") + "\" Architecture: \"" + System.getProperty("os.arch") + "\"");
System.out.println("Sun JCE: [" + Security.getProvider("SunJCE") + "] Info: [" + Security.getProvider("SunJCE").getInfo() + "]");
String[] algorithmsToTest = { "PBEWITHMD5ANDTRIPLEDES", "PBEWITHSHA1ANDDESEDE", "PBEWITHMD5ANDDES", "PBEWITHSHA1ANDRC2_40" };
for (String algorithmRequested : algorithmsToTest) {
try {
// Random salt...
final byte[] salt = {
(byte)0xA5, (byte)0x9E, (byte)0xC8, (byte)0x32,
(byte)0x43, (byte)0xF5, (byte)0xEA, (byte)0x03
};
// Create the encryption key from the passphrase...
KeySpec keySpec = new PBEKeySpec("Test passphrase".toCharArray(), salt, 19);
SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithmRequested);
String algorithmReturnedByFactory = factory.getAlgorithm();
SecretKey key = factory.generateSecret(keySpec);
String algorithmReturnedByKey = key.getAlgorithm();
System.out.println();
System.out.println("algorithmRequested: " + algorithmRequested);
System.out.println("algorithmReturnedByFactory: " + algorithmReturnedByFactory);
System.out.println("algorithmReturnedByKey: " + algorithmReturnedByKey);
if (algorithmRequested.equalsIgnoreCase(algorithmReturnedByKey)) {
System.out.println("NO BUG FOUND.");
}
else {
System.out.println("BUG FOUND: Algorithm returned by key differs from algorithm requested.");
}
}
catch (Exception e) {
System.out.println("Exception for algorithm [" + algorithmRequested + "]: " + e);
}
}
}
}
And here is the output:
=====================
JDK 1.5...
JVM Name: "Java HotSpot(TM) 64-Bit Server VM" Version: "1.5.0_09-b03" Vendor: "Sun Microsystems Inc."
OS Name: "Linux" Version: "2.6.19-1.2895.fc6" Architecture: "amd64"
Sun JCE: [SunJCE version 1.5] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
algorithmRequested: PBEWITHMD5ANDTRIPLEDES
algorithmReturnedByFactory: PBEWITHMD5ANDTRIPLEDES
algorithmReturnedByKey: PBEWithMD5AndDES
BUG FOUND: Algorithm returned by key differs from algorithm requested.
algorithmRequested: PBEWITHSHA1ANDDESEDE
algorithmReturnedByFactory: PBEWITHSHA1ANDDESEDE
algorithmReturnedByKey: PBEWithMD5AndDES
BUG FOUND: Algorithm returned by key differs from algorithm requested.
algorithmRequested: PBEWITHMD5ANDDES
algorithmReturnedByFactory: PBEWITHMD5ANDDES
algorithmReturnedByKey: PBEWithMD5AndDES
NO BUG FOUND.
algorithmRequested: PBEWITHSHA1ANDRC2_40
algorithmReturnedByFactory: PBEWITHSHA1ANDRC2_40
algorithmReturnedByKey: PBEWithMD5AndDES
BUG FOUND: Algorithm returned by key differs from algorithm requested.
=====================
JDK 1.6...
JVM Name: "Java HotSpot(TM) 64-Bit Server VM" Version: "1.6.0-b105" Vendor: "Sun Microsystems Inc."
OS Name: "Linux" Version: "2.6.19-1.2895.fc6" Architecture: "amd64"
Sun JCE: [SunJCE version 1.6] Info: [SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)]
algorithmRequested: PBEWITHMD5ANDTRIPLEDES
algorithmReturnedByFactory: PBEWITHMD5ANDTRIPLEDES
algorithmReturnedByKey: PBEWithMD5AndTripleDES
NO BUG FOUND.
algorithmRequested: PBEWITHSHA1ANDDESEDE
algorithmReturnedByFactory: PBEWITHSHA1ANDDESEDE
algorithmReturnedByKey: PBEWithSHA1AndDESede
NO BUG FOUND.
algorithmRequested: PBEWITHMD5ANDDES
algorithmReturnedByFactory: PBEWITHMD5ANDDES
algorithmReturnedByKey: PBEWithMD5AndDES
NO BUG FOUND.
algorithmRequested: PBEWITHSHA1ANDRC2_40
algorithmReturnedByFactory: PBEWITHSHA1ANDRC2_40
algorithmReturnedByKey: PBEWithSHA1AndRC2_40
NO BUG FOUND.
There don't seem to be any existing bug reports for this. This is a serious bug - it has caused us at this company to encrypt our data with 56-bit security instead of 112-bit security. It will affect anyone using Key.getAlgorithm() in their encryption code.
The bug has been fixed in JDK 6, but there is a workaround for JDK 5: don't use key.getAlgorithm() !
In my previous test case, changing...
Cipher ecipher = Cipher.getInstance(key.getAlgorithm());
to
Cipher ecipher = Cipher.getInstance(algorithm);
...fixes the problem.
Note that with this workaround in place in my original test case, requesting PBEWITHMD5ANDTRIPLEDES with JDK 5 without policy files *NOW* causes the "Illegal key size" exception, because it actually tries to use the TripleDES algorithm.
I'd imaging that you guys who have been 'successfully' using TripleDES on JDK 5 without policy files installed, have actually been using DES encryption without knowing it.