加密在唯一文件中附加 IVSBytes 的文件 - Execption:给定的最终块未正确填充

Encrypt a file appending IVSBytes in unique file - Execption: Given final block not properly padded

我正在尝试使用带有 SHA-256 密钥的 AES 加密文件。 当我生成 IV 时,我将其添加到加密文件的开头,然后附加其余部分。

当我恢复 IV 并比较它时(过程之后和之后的 IV)它们是相同的。问题是当我尝试解密文件时:

javax.crypto.BadPaddingException: Given final block not properly padded

我想可能是因为我没有正确读取以下字节,但我修改了代码,似乎还可以。

加密货币class:

private static String password;
private static String salt;
private static int pswdIterations = 65536  ;
private static int keySize = 256;
private byte[] ivBytes;

public void encryptOfFile(byte[] bytes, File out) throws Exception {  

    byte[] saltBytes = salt.getBytes("UTF-8");

    System.out.println("Salt bfre:" +salt);

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            saltBytes,
            pswdIterations,
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");

    //encrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();

    //First copy the IVBytes at the beginning  of the file
    System.out.println("IvBytes Bfore: "  + DatatypeConverter.printHexBinary(ivBytes));
    FileOutputStream os = new FileOutputStream(out, true);
    os.write(ivBytes);

    CipherOutputStream cos = new CipherOutputStream(os, cipher);
    cos.write(bytes);

    os.close();
    cos.close();

}

public byte[] decryptToBytes(File in) throws Exception {  
    byte[] saltBytes = salt.getBytes("UTF-8");
    System.out.println("Salt afetr:" +salt);

    // Derive the key
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            saltBytes,
            pswdIterations,
            keySize
            );

    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");


    //Get IVBytes of the first 16 bytes of the file
    System.out.println("File Size: "  + in.length());

    FileInputStream is = new FileInputStream(in);
    byte [] ivBytesRecovered = new byte [16];
    if (is.read(ivBytesRecovered) != ivBytesRecovered.length) {
        //is.close();
        throw new IllegalStateException("Too short file");
    }
    System.out.println("IvBytes After: "  + DatatypeConverter.printHexBinary(ivBytesRecovered));

    // Decrypt the message
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytesRecovered));        

    byte[] encBytes = new byte[(int) in.length()-16];
    is.read(encBytes);

    byte[] decryptedBytes = null;
    try {
        decryptedBytes = cipher.doFinal(encBytes);
    } catch (IllegalBlockSizeException | BadPaddingException e) {
        e.printStackTrace();
    }

    is.close();

    return decryptedBytes;

}

我收到了错误消息...可能是因为我没有正确读取前 16 个字节之后的字节?或者可能是因为我没有为 byte[] encBytes 提供合适的大小?

用于生成盐:

public String generateSalt() {
    SecureRandom random = new SecureRandom();
    byte bytes[] = new byte[20];
    random.nextBytes(bytes);
    String s = new String(bytes);
    return s;
}

你的问题在encrypt/write这边;你 close os (FileOutputStream) 在你 close cos (CipherOutputStream) 之前, 所以它不会将最后一个加密块写入文件。相反,你应该 cos.close(); 每个 javadoc (请注意,为您处理 os.close()):

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.

或者因为您的明文适合一个缓冲区(并且您的读取端 需要 )跳过 CipherOutputStream 并且只是 os.write (cipher.doFinal(bytes)); os.close();

此外,您使用 (file,true) 构造 FileOutputStream,这意味着如果识别的文件已经存在并且是非空的(并且未受保护,因此它会抛出异常),您的新数据将 appended 在最后,但您的阅读方希望它在开头。

而且:你没有显示 salt 是什么,但如果它包含的熵没有超过人类记忆的程度,它可能就没有发挥作用ose。由于我们不能指望人类记住并输入足够的盐,因此通常的做法是将其包含在 file/message/database/whatever.