Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6634037

SunJCE: AES/CTR/PKCS5Padding - the padding PKCS5Padding is not used

    Details

    • Type: Bug
    • Status: Open
    • Priority: P3
    • Resolution: Unresolved
    • Affects Version/s: 6
    • Fix Version/s: None
    • Component/s: security-libs
    • Labels:
    • Subcomponent:
    • Understanding:
      Cause Known
    • CPU:
      x86
    • OS:
      windows_xp

      Description

      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.

        Attachments

          Activity

            People

            • Assignee:
              wetmore Bradford Wetmore
              Reporter:
              wetmore Bradford Wetmore
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Imported:
                Indexed: