我正在编写一个简单的自定义加密器/解密器。
基本上,我只是提取一个大文件的前 1024 个字节并对其进行加密。
我使用了 RandomAccessFile,以便我可以快速加密和解密前 1024 个字节。
现在,我面临的问题是,即使我使用相同的算法进行加密和解密。
加密工作正常,但解密会抛出一个 javax.crypto.BadPaddingException:Given final block not properly padded 。
无论我搜索多少,我都无法弄清楚哪里出了问题。对此的一些研究告诉我,由于 UTF 和 base64 等格式不同,填充不正确。但是我不确定如果我读取这样一个大文件的前 1024 个字节并且加密没有抛出异常,那么填充是如何不正确的。此外,我没有对字符串进行任何转换。
我在下面提供了简单的注释和代码
public class Encryptor {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";
public void encrypt(String key, File inputFile, File outputFile) throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public void decrypt(String key, File inputFile, File outputFile) throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private void doCrypto(int cipherMode, String key, File inputFile, File outputFile) throws CryptoException {
try {
Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(cipherMode, secretKey);
byte[] inputBytes = new byte[16];
byte[] outputBytes = new byte[16];
//Open the file in read write mode
RandomAccessFile fileStore = new RandomAccessFile(inputFile, "rw");
fileStore.seek(0);
//encrypt first 1024bytes
int bytesRead = 0;
for(int ctr=0;bytesRead!= -1 && ctr<64 ;ctr++){
//get file pointer position
long prevPosition = fileStore.getFilePointer();
//read 16 bytes to array
bytesRead = fileStore.read(inputBytes);
//if successful, go move back pointer and overwrite these 16 bytes with encrypted bytes
if(bytesRead != 1){
outputBytes = cipher.doFinal(inputBytes);
fileStore.seek(prevPosition);
fileStore.write(outputBytes);
}
}
fileStore.close();
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException ex) {
throw new CryptoException(ex);
}
}
最佳答案
首先,"AES"
算法字符串由 SUN 提供商在内部转换为 "AES/ECB/PKCS5Padding"
。这意味着您使用的是不安全的 ECB 模式加密,而不是更安全的 CBC 模式。
你得到BadPaddingException
的原因很简单。当您使用 PKCS5Padding
(或更准确地说是 PKCS#7 填充)时,每 16 个字节在加密期间被填充到 32 个字节。但是,您只存储未填充的 16 个字节。如果您尝试解密 unpadding 机制会尝试 unpad 原始未填充的明文,这将失败。
其次,read
方法实际上可能读取不到 16 个字节。它只读取最多 16 个字节。您需要创建一个单独的方法来始终精确读取 16 个字节。
多次调用doFinal
不是一个好主意。您最好一次读取 1024 个字节,然后调用 doFinal
或 - 甚至更好 - update
一次。在那种情况下,您应该使用例如"AES/CBC/NoPadding"
作为算法字符串。
注意事项:
- 如果没有随机 IV,您仍然能够区分以相同字节开头的文件(或以相同字节开头的及时重新加密的文件);
- 您可能需要某种协议(protocol)来处理小于 1024 字节的文件;
- 使用“try-with-resources”可能是一个非常好的主意;
- 使用文件的内存映射来实现更简洁的设计和(可能)更快的操作。
关于java - 对文件使用相同的 AES 算法的错误填充异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37686794/