last block incomplete with CipherInputStream/CipherOutputStream, even with padding AES/CBC/PKCS5Padding

让人想犯罪 __ 提交于 2019-11-30 03:29:25

I've had exactly the same problem. The accepted solution works because you've used cipher mode that does not require padding, but this is not a way crypto-related issues are fixed.

According to the CipherOutputStream documentation, you have to call close() method in order to finalize encryption properly (i.e., padding block is added).

This method invokes the doFinal method of the encapsulated cipher object, which causes any bytes buffered by the encapsulated cipher to be processed. The result is written out by calling the flush method of this output stream.

This method resets the encapsulated cipher object to its initial state and calls the close method of the underlying output stream.

If you want to preserve OutputStream open even after calling CipherOutputStream.close() method, you can wrap OutputStream to the stream that does not close it. For example:

public class NotClosingOutputStream extends OutputStream {
  private final OutputStream os;

  public NotClosingOutputStream(OutputStream os) {
    this.os = os;
  }

  @Override
  public void write(int b) throws IOException {
    os.write(b);
  }

  @Override
  public void close() throws IOException {
    // not closing the stream.
  }

  @Override
  public void flush() throws IOException {
    os.flush();
  }

  @Override
  public void write(byte[] buffer, int offset, int count) throws IOException {
    os.write(buffer, offset, count);
  }

  @Override
  public void write(byte[] buffer) throws IOException {
    os.write(buffer);
  }
}

Then you can use:

...
cos = new CipherOutputStream(new NotClosingOutputStream(os), encipher);
copyByte(is, cos);
cos.close();
...

Note the os stream does not get closed, you need to do it on your own when appropriate.

Finally I got answer for my own question, with trial and error Actually here Conflict is I set Padding in encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

and Set IV with some values.....,

Finally I got Answer only just replaced the Algorithm

From:

AES/CBC/PKCS7Paddinng

To:

AES/CFB8/NoPadding

and its worked like charm...., So I suggest this answer for others who struggling with this problem, if you solved you problem, mention here for others...

I've seen CipherInputStream fail with padding problems too. This behaviour varied across different versions of the JVM. Eg 7u55 32 bit my code worked fine, 7u55 64 bit same code failed... and I also saw failures on later 32 bit JVMs. Workaround was to use byte array methods and avoid CipherInputStream.

Not sure if this is relevant to OP's problem, but this may help someone.

When you repeatedly get that java.io.IOException: last block incomplete in decryption regardless of what you change, check if you are still using the file from some previous run. If your read/write test code appends to that file, you will always get that exception -- unless you delete the corrupt file that you write to.

Using CipherInputStream with padding is possible, switching to NoPadding is a workaround but not a solution.

Padding is applied when CipherInputStream reaches the end of the stream. The important point is that you have to call the read() method of the CipherInputStream at least twice for getting all the data.

The following example demonstrates reading a CipherInputStream with padding:

public static void test() throws Exception {
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

    SecureRandom rnd = new SecureRandom();
    byte[] keyData = new byte[16];
    byte[] iv = new byte[16];
    rnd.nextBytes(keyData);
    rnd.nextBytes(iv);
    SecretKeySpec key = new SecretKeySpec(keyData, "AES");

    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    CipherOutputStream out = new CipherOutputStream(buffer, cipher);

    byte[] plain = "Test1234567890_ABCDEFG".getBytes();
    out.write(plain);
    out.flush();
    out.close();
    byte[] encrypted = buffer.toByteArray();
    System.out.println("Plaintext length: " + plain.length);
    System.out.println("Padding length  : " + (cipher.getBlockSize() - (plain.length % cipher.getBlockSize())));
    System.out.println("Cipher length   : " + encrypted.length);

    cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
    CipherInputStream in = new CipherInputStream(new ByteArrayInputStream(encrypted), cipher);
    buffer = new ByteArrayOutputStream();
    byte[] b = new byte[100];
    int read;
    while ((read = in.read(b)) >= 0) {
        buffer.write(b, 0, read);
    }
    in.close();

    // prints Test1234567890_ABCDEFG
    System.out.println(new String(buffer.toByteArray()));
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!