0

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

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

javax.crypto.BadPaddingException:给定最终块未正确填充

我认为这可能是因为我没有正确读取以下字节,但我修改了代码,看起来还可以。

加密类:

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

1 回答 1

1

您的问题出在加密/写入方面;在关闭 cos(CipherOutputStream)之前关闭 os(FileOutputStream),因此它不会将最后一个加密块写入文件。相反,你应该只是cos.close();每个javadoc (注意为你处理 os.close() ):

此方法调用封装密码对象的 doFinal 方法,这会导致处理封装密码缓冲的任何字节。通过调用此输出流的 flush 方法写出结果。

此方法将封装的密码对象重置为其初始状态并调用底层输出流的关闭方法。

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

此外,您构造 FileOutputStream ,(file,true)这意味着如果识别的文件已经存在并且是非空的(并且没有受到保护,因此会引发异常),您的新数据将附加在末尾,但您的读取方希望它位于开头。

并且:你没有展示是什么,但如果它不包含比人类记忆更多的熵,它可能无法达到它的目的。由于我们不能指望人类记住并输入足够的盐,因此通常的做法是将其包含在文件/消息/数据库/任何内容中。

于 2015-04-09T18:04:14.910 回答