Последний незавершенный блок с CipherInputStream/CipherOutputStream, даже с дополнением AES/CBC/PKCS5Padding

Собственно, я искал много из Интернета и в stackoverflow тоже для этого,

Изначально я не использовал прописку в моем шифровании и расшифровке,

Но наконец я получил решение отсюда

qaru.site/info/440782/...

и я обновил свой код с помощью дополнения как AES/CBC/PKCS5Padding и такая же ошибка наступает, и последний блок не дешифрован...

Я работаю над этим в течение последних двух дней, но решения не найдено

мой код Crypter:

package mani.droid.browsedropbox;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Crypter {

    Cipher encipher;
    Cipher decipher;
CipherInputStream cis;
CipherOutputStream cos;
FileInputStream fis;
byte[] ivbytes = new byte[]{(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e',                                                                                          (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m',     (byte)'n', (byte)'o', (byte)'p'};
    IvParameterSpec iv = new IvParameterSpec(ivbytes);

public boolean enCrypt(String key, InputStream is, OutputStream os)
{
    try {
        byte[] encoded = new BigInteger(key, 16).toByteArray();
        SecretKey seckey = new SecretKeySpec(encoded, "AES");
        encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        encipher.init(Cipher.ENCRYPT_MODE, seckey, iv);
        cis = new CipherInputStream(is, encipher);
        copyByte(cis, os);
        return true;
    } 
    catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return false;
}

public boolean deCrypt(String key, InputStream is, OutputStream os)
{
    try {
        byte[] encoded = new BigInteger(key, 16).toByteArray();
        SecretKey seckey = new SecretKeySpec(encoded, "AES");
        encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        encipher.init(Cipher.DECRYPT_MODE, seckey, iv);
        cos = new CipherOutputStream(os, encipher);
        copyByte(is, cos);
        //cos.close();
        return true;
    } 
    catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return false;
}

public void copyByte(InputStream is, OutputStream os) throws IOException
{
    byte[] buf = new byte[8192];
    int numbytes;
    while((numbytes = is.read(buf)) != -1)
    {
        os.write(buf, 0, numbytes);
        os.flush();
    }
    os.close();
    is.close();
}
}

Ответ 1

Наконец, я получил ответ на свой вопрос, с проб и ошибок На самом деле здесь Конфликт - это установка "Заполнение" в encipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

и Set IV с некоторыми значениями.....,

Наконец, я получил ответ, который только что заменил Алгоритм

From:

AES/CBC/PKCS7Paddinng

To:

AES/CFB8/NoPadding

и его работа была похожа на очарование...., поэтому я предлагаю этот ответ другим, которые борются с этой проблемой, если вы решили проблему, упомяните здесь для других...

Ответ 2

У меня была точно такая же проблема. Принятое решение работает, потому что вы использовали режим шифрования, который не требует заполнения, но это не так, как исправлены связанные с криптованием проблемы.

Согласно документации CipherOutputStream вам нужно вызвать метод close(), чтобы правильно завершить шифрование (т.е. добавлен блок дополнений).

Этот метод вызывает метод doFinal инкапсулированного шифра объект, который вызывает любые байты, буферизированные инкапсулированным шифром, обрабатываться. Результат выписывается путем вызова метода flush для этот выходной поток.

Этот метод сбрасывает инкапсулированный объект шифрования в исходное состояние и вызывает метод закрытия основного потока вывода.

Если вы хотите сохранить OutputStream открытым даже после вызова метода CipherOutputStream.close(), вы можете перенести OutputStream в поток, который не закрывает его. Например:

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);
  }
}

Затем вы можете использовать:

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

Обратите внимание, что поток os не закрывается, вам нужно сделать это самостоятельно, когда это необходимо.

Ответ 3

Я видел, как CipherInputStream терпит неудачу и с проблемами заполнения. Такое поведение варьировалось в разных версиях JVM. Например, 7u55 32-битный мой код работал нормально, 7u55 64-разрядный же код не удалось... и я также видел сбои на более поздних 32-битных JVM. Обходным путем было использование методов массива байтов и исключение CipherInputStream.

Ответ 4

Не уверен, что это относится к проблеме OP, но это может помочь кому-то.

Когда вы повторно получите java.io.IOException: last block incomplete in decryption , независимо от того, что вы измените, проверьте, используете ли вы файл с предыдущего запуска. Если ваш тестовый код чтения/записи добавляется к этому файлу, вы всегда получите это исключение - если вы не удалите поврежденный файл, который вы пишете.

Ответ 5

Использование CipherInputStream с дополнением возможно, переход на NoPadding является обходным решением, но не решением.

Заполнение применяется, когда CipherInputStream достигает конца потока. Важным моментом является то, что вы должны вызывать метод read() CipherInputStream как минимум дважды для получения всех данных.

Следующий пример демонстрирует чтение CipherInputStream с дополнением:

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()));
}