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

Partially decrypting stream using Blowfish throws BadPaddingException on close

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 7u71, 8u31
    • security-libs

      FULL PRODUCT VERSION :
      java version "1.7.0_71"
      Java(TM) SE Runtime Environment (build 1.7.0_71-b14)
      Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.3.9600]

      A DESCRIPTION OF THE PROBLEM :
      Closing a cipher input stream early before reading all content will throw a BadPaddingException when closing the stream. The encryption used is Blowfish.

      REGRESSION. Last worked in version 7u67

      ADDITIONAL REGRESSION INFORMATION:
      java version "1.7.0_67"
      Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
      Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See attached sample.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The stream should not throw an exception on close unless all data has been read.
      ACTUAL -
      The stream throws a BadPaddingException when calling close on the cipher input stream.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Exception in thread "main" java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded
      at javax.crypto.CipherInputStream.close(CipherInputStream.java:321)
      at test.TestBlowfish.main(TestBlowfish.java:45)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:606)
      at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
      Caused by: javax.crypto.BadPaddingException: Given final block not properly padded
      at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
      at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
      at com.sun.crypto.provider.BlowfishCipher.engineDoFinal(BlowfishCipher.java:319)
      at javax.crypto.Cipher.doFinal(Cipher.java:1970)
      at javax.crypto.CipherInputStream.close(CipherInputStream.java:314)
      ... 6 more

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package test;

      import javax.crypto.Cipher;
      import javax.crypto.CipherInputStream;
      import javax.crypto.CipherOutputStream;
      import javax.crypto.spec.IvParameterSpec;
      import javax.crypto.spec.SecretKeySpec;
      import java.io.*;
      import java.util.Arrays;

      public class TestBlowfish {
          private static final boolean ENABLE_BUG = true;

          public static void main(String[] args) throws Exception {
              final byte[] original = new byte[64 * 1024];
              for (int i = 0; i < original.length; i++) {
                  original[i] = (byte) i;
              }
              final byte ivBytes[] = new byte[]{8, 7, 6, 5, 4, 3, 2, 1};
              final SecretKeySpec key = new SecretKeySpec("password".getBytes(), "Blowfish");
              final IvParameterSpec iv = new IvParameterSpec(ivBytes);
              final Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");

              final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
              cipher.init(Cipher.ENCRYPT_MODE, key, iv);
              try (OutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher)) {
                  cipherOutputStream.write(original);
                  cipherOutputStream.write(original);
              }

              final byte[] decrypt = new byte[64 * 1024];
              cipher.init(Cipher.DECRYPT_MODE, key, iv);
              try (InputStream cipherInputStream = new CipherInputStream(new ByteArrayInputStream(outputStream.toByteArray()), cipher)) {
                  readAll(cipherInputStream, decrypt);
                  if (!Arrays.equals(original, decrypt)) {
                      throw new IllegalStateException("decrypt error");
                  }

                  if (!ENABLE_BUG) {
                      readAll(cipherInputStream, decrypt);
                      if (!Arrays.equals(original, decrypt)) {
                          throw new IllegalStateException("decrypt error");
                      }
                  }
              }
          }

          private static void readAll(InputStream inputStream, byte[] buffer) throws IOException {
              int read = 0;
              while (read != buffer.length) {
                  read += inputStream.read(buffer, read, buffer.length - read);
              }
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The code must be changed to handle this case explicitly and this is impossible for any deployed application without updating it.

      The BadPaddingException must be explicitly caught and ignored when calling close on a cipher input stream.

      SUPPORT :
      YES

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: