-
Bug
-
Resolution: Won't Fix
-
P3
-
None
-
6
-
x86
-
windows_xp
FULL PRODUCT VERSION :
Tested on
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_09-b03)
and
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
ADDITIONAL OS VERSION INFORMATION :
Windows XP, Linux Suse
A DESCRIPTION OF THE PROBLEM :
We are trying to use AES/CTR/PKCS5Padding with the SunJCE. Our customer is using BouncyCastle to decrypt the message. But somehow it does not work. So we wrote some test code to check it and found, that the same message crypted with SunJCE leads to a different crypted message compared to BouncyCastle.
JDK - PKCS5Padding: xACPevu34yhaM6SheR0R3huDu8f4nxk=
BC - PKCS5Padding: xACPevu34yhaM6SheR0R3huDu8f4nxm/5okbibDYEIE=
JDK - NoPadding : xACPevu34yhaM6SheR0R3huDu8f4nxk=
BC - NoPadding : xACPevu34yhaM6SheR0R3huDu8f4nxk=
It seems, that the SunJCE does not apply the PKCS5Padding. When using BC to decrypt it, we get javax.crypto.IllegalBlockSizeException: last block incomplete in decryption.
Is the SunJCE wrong because it does not care about the PKCS5Padding? Or is BouncyCastle wrong? Our crypto knowledge is limited, so it might even be, that both providers are right somehow.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute test case and install the BouncyCastle library too (http://www.bouncycastle.org/java.html). Make sure that the export restriction update to java security has been applied.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Both libraries can decrypt the encrypted message from the other provider when using the same key and algorithm/padding.
ACTUAL -
SunJCE does not seem to apply the PKSC5Padding before encrypting the message, because the result with NoPadding is exactly the same.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package crypto;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.junit.Assert;
import org.junit.Test;
public class BCandJDKCompare
{
public byte[] jdkEncrypt(final String algorithmName, final String message, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "SunJCE");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] cipherText = cipher.doFinal(message.getBytes());
return Base64.encodeBase64(cipherText);
}
public byte[] jdkDecrypt(final String algorithmName, byte[] encrypted, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "SunJCE");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keySpec, iv);
byte[] result = cipher.doFinal(Base64.decodeBase64(encrypted));
return result;
}
public byte[] bcEncrypt(final String algorithmName, final String message, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "BC");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] cipherText = cipher.doFinal(message.getBytes());
return Base64.encodeBase64(cipherText);
}
public byte[] bcDecrypt(final String algorithmName, byte[] encrypted, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "BC");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
byte[] result = cipher.doFinal(Base64.decodeBase64(encrypted));
return result;
}
public byte[] generateSecretKey(String algorithmName, int keyLength)
throws Exception
{
javax.crypto.KeyGenerator keyGen = javax.crypto.KeyGenerator.getInstance(algorithmName);
keyGen.init(keyLength);
SecretKey key = keyGen.generateKey();
return Base64.encodeBase64(key.getEncoded());
}
@Test
public void testDifferentPaddings() throws Exception
{
final String message = "This text has 23 bytes."; final byte[] saltIV = "0000000000000044".getBytes(); byte[] key = generateSecretKey("AES", 128);
// this section works fine, because each provider deals with its own messages
// it also shows the different length of the resulting messages
{
String algorithmName = "AES/CTR/PKCS5Padding";
byte[] jdkEncrypted5 = jdkEncrypt(algorithmName, message, key, saltIV);
byte[] jdkDecrypted5 = jdkDecrypt(algorithmName, jdkEncrypted5, key, saltIV);
System.out.println("JDK - PKCS5Padding: " + new String(jdkEncrypted5));
Assert.assertEquals(message, new String(jdkDecrypted5));
byte[] bcEncrypted5 = bcEncrypt(algorithmName, message, key, saltIV);
byte[] bcDecrypted5 = bcDecrypt(algorithmName, bcEncrypted5, key, saltIV);
System.out.println("BC - PKCS5Padding: " + new String(bcEncrypted5));
Assert.assertEquals(message, new String(bcDecrypted5));
algorithmName = "AES/CTR/NoPadding";
byte[] jdkEncryptedNo = jdkEncrypt(algorithmName, message, key, saltIV);
byte[] jdkDecryptedNo = jdkDecrypt(algorithmName, jdkEncryptedNo, key, saltIV);
System.out.println("JDK - NoPadding: " + new String(jdkEncryptedNo));
Assert.assertEquals(message, new String(jdkDecryptedNo));
byte[] bcEncryptedNo = bcEncrypt(algorithmName, message, key, saltIV);
byte[] bcDecryptedNo = bcDecrypt(algorithmName, bcEncryptedNo, key, saltIV);
System.out.println("BC - NoPadding: " + new String(bcEncryptedNo));
Assert.assertEquals(message, new String(bcDecryptedNo));
}
}
@Test
public void testSunJCEtoBC() throws Exception
{
final String message = "This text has 23 bytes."; final byte[] saltIV = "0000000000000044".getBytes(); byte[] key = generateSecretKey("AES", 128);
{
String algorithmName = "AES/CTR/PKCS5Padding";
byte[] jdkEncrypted5 = jdkEncrypt(algorithmName, message, key, saltIV);
// BC cannot not decrypt the message encrypted by the SunJCE
byte[] bcDecryptedFromJDK = bcDecrypt(algorithmName, jdkEncrypted5, key, saltIV);
Assert.assertEquals(message, new String(bcDecryptedFromJDK));
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Switch to BouncyCastle completely. But it is not quite clear, whether or not the SunJCE might be right.
Release Regression From : 5.0u6
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.
Release Regression From : 5.0u6
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.
Release Regression From : 5.0u6
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.
Tested on
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_09-b03)
and
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
ADDITIONAL OS VERSION INFORMATION :
Windows XP, Linux Suse
A DESCRIPTION OF THE PROBLEM :
We are trying to use AES/CTR/PKCS5Padding with the SunJCE. Our customer is using BouncyCastle to decrypt the message. But somehow it does not work. So we wrote some test code to check it and found, that the same message crypted with SunJCE leads to a different crypted message compared to BouncyCastle.
JDK - PKCS5Padding: xACPevu34yhaM6SheR0R3huDu8f4nxk=
BC - PKCS5Padding: xACPevu34yhaM6SheR0R3huDu8f4nxm/5okbibDYEIE=
JDK - NoPadding : xACPevu34yhaM6SheR0R3huDu8f4nxk=
BC - NoPadding : xACPevu34yhaM6SheR0R3huDu8f4nxk=
It seems, that the SunJCE does not apply the PKCS5Padding. When using BC to decrypt it, we get javax.crypto.IllegalBlockSizeException: last block incomplete in decryption.
Is the SunJCE wrong because it does not care about the PKCS5Padding? Or is BouncyCastle wrong? Our crypto knowledge is limited, so it might even be, that both providers are right somehow.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute test case and install the BouncyCastle library too (http://www.bouncycastle.org/java.html). Make sure that the export restriction update to java security has been applied.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Both libraries can decrypt the encrypted message from the other provider when using the same key and algorithm/padding.
ACTUAL -
SunJCE does not seem to apply the PKSC5Padding before encrypting the message, because the result with NoPadding is exactly the same.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package crypto;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.junit.Assert;
import org.junit.Test;
public class BCandJDKCompare
{
public byte[] jdkEncrypt(final String algorithmName, final String message, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "SunJCE");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] cipherText = cipher.doFinal(message.getBytes());
return Base64.encodeBase64(cipherText);
}
public byte[] jdkDecrypt(final String algorithmName, byte[] encrypted, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "SunJCE");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keySpec, iv);
byte[] result = cipher.doFinal(Base64.decodeBase64(encrypted));
return result;
}
public byte[] bcEncrypt(final String algorithmName, final String message, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "BC");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] cipherText = cipher.doFinal(message.getBytes());
return Base64.encodeBase64(cipherText);
}
public byte[] bcDecrypt(final String algorithmName, byte[] encrypted, byte[] key, byte[] salt) throws Exception {
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(algorithmName, "BC");
IvParameterSpec iv = new IvParameterSpec(salt);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
byte[] result = cipher.doFinal(Base64.decodeBase64(encrypted));
return result;
}
public byte[] generateSecretKey(String algorithmName, int keyLength)
throws Exception
{
javax.crypto.KeyGenerator keyGen = javax.crypto.KeyGenerator.getInstance(algorithmName);
keyGen.init(keyLength);
SecretKey key = keyGen.generateKey();
return Base64.encodeBase64(key.getEncoded());
}
@Test
public void testDifferentPaddings() throws Exception
{
final String message = "This text has 23 bytes."; final byte[] saltIV = "0000000000000044".getBytes(); byte[] key = generateSecretKey("AES", 128);
// this section works fine, because each provider deals with its own messages
// it also shows the different length of the resulting messages
{
String algorithmName = "AES/CTR/PKCS5Padding";
byte[] jdkEncrypted5 = jdkEncrypt(algorithmName, message, key, saltIV);
byte[] jdkDecrypted5 = jdkDecrypt(algorithmName, jdkEncrypted5, key, saltIV);
System.out.println("JDK - PKCS5Padding: " + new String(jdkEncrypted5));
Assert.assertEquals(message, new String(jdkDecrypted5));
byte[] bcEncrypted5 = bcEncrypt(algorithmName, message, key, saltIV);
byte[] bcDecrypted5 = bcDecrypt(algorithmName, bcEncrypted5, key, saltIV);
System.out.println("BC - PKCS5Padding: " + new String(bcEncrypted5));
Assert.assertEquals(message, new String(bcDecrypted5));
algorithmName = "AES/CTR/NoPadding";
byte[] jdkEncryptedNo = jdkEncrypt(algorithmName, message, key, saltIV);
byte[] jdkDecryptedNo = jdkDecrypt(algorithmName, jdkEncryptedNo, key, saltIV);
System.out.println("JDK - NoPadding: " + new String(jdkEncryptedNo));
Assert.assertEquals(message, new String(jdkDecryptedNo));
byte[] bcEncryptedNo = bcEncrypt(algorithmName, message, key, saltIV);
byte[] bcDecryptedNo = bcDecrypt(algorithmName, bcEncryptedNo, key, saltIV);
System.out.println("BC - NoPadding: " + new String(bcEncryptedNo));
Assert.assertEquals(message, new String(bcDecryptedNo));
}
}
@Test
public void testSunJCEtoBC() throws Exception
{
final String message = "This text has 23 bytes."; final byte[] saltIV = "0000000000000044".getBytes(); byte[] key = generateSecretKey("AES", 128);
{
String algorithmName = "AES/CTR/PKCS5Padding";
byte[] jdkEncrypted5 = jdkEncrypt(algorithmName, message, key, saltIV);
// BC cannot not decrypt the message encrypted by the SunJCE
byte[] bcDecryptedFromJDK = bcDecrypt(algorithmName, jdkEncrypted5, key, saltIV);
Assert.assertEquals(message, new String(bcDecryptedFromJDK));
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Switch to BouncyCastle completely. But it is not quite clear, whether or not the SunJCE might be right.
Release Regression From : 5.0u6
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.
Release Regression From : 5.0u6
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.
Release Regression From : 5.0u6
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.