Java 对文件使用相同的AES算法时出现错误的填充异常?

Java 对文件使用相同的AES算法时出现错误的填充异常?,java,algorithm,encryption,cryptography,Java,Algorithm,Encryption,Cryptography,我正在编写一个简单的自定义加密/解密程序。 基本上,我只需要获取大文件的前1024个字节并对其进行加密。 我使用了一个RandomAccessFile,这样我可以快速加密和解密前1024字节 现在,我面临的问题是,即使我使用相同的加密和解密算法。 加密工作正常,但解密抛出一个javax.crypto.badpattingexception:给定的最后一个块没有正确填充 不管我搜索了多少,我都不知道哪里出了问题。对此的一些研究告诉我,由于UTF和base64等格式不同,填充是不正确的。但我不确定,

我正在编写一个简单的自定义加密/解密程序。
基本上,我只需要获取大文件的前1024个字节并对其进行加密。
我使用了一个RandomAccessFile,这样我可以快速加密和解密前1024字节

现在,我面临的问题是,即使我使用相同的加密和解密算法。
加密工作正常,但解密抛出一个javax.crypto.badpattingexception:给定的最后一个块没有正确填充

不管我搜索了多少,我都不知道哪里出了问题。对此的一些研究告诉我,由于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”;
私有静态最终字符串转换=“AES”;
public void encrypt(字符串密钥、文件输入文件、文件输出文件)引发加密异常{
doCrypto(Cipher.ENCRYPT_模式、密钥、输入文件、输出文件);
}
公共无效解密(字符串密钥、文件输入文件、文件输出文件)引发加密异常{
doCrypto(Cipher.DECRYPT_模式、密钥、输入文件、输出文件);
}    
私有void doCrypto(int-cipherMode、字符串密钥、文件输入文件、文件输出文件)引发加密异常{
试一试{
Key secretKey=new SecretKeySpec(Key.getBytes(),算法);
Cipher Cipher=Cipher.getInstance(转换);
cipher.init(cipherMode,secretKey);
字节[]输入字节=新字节[16];
字节[]输出字节=新字节[16];
//以读写模式打开文件
RandomAccessFile文件存储=新的RandomAccessFile(输入文件,“rw”);
查找(0);
//加密前1024字节
int字节读取=0;
对于(int-ctr=0;bytesRead!=-1&&ctr首先,SUN提供程序将
“AES”
算法字符串转换为
“AES/ECB/PKCS5Padding”
。这意味着您使用的是不安全的ECB模式加密,而不是更安全的CBC模式

获得
BadPaddingException
的原因很简单。使用
PKCS5Padding
(或者更准确地说是PKCS#7 padding),在加密过程中,每16个字节最多填充32个字节。但是,您仅存储未添加的16个字节。如果尝试解密未添加的机制,则会尝试取消添加原始未添加的明文,这将失败

其次,
read
方法实际上可能无法读取16个字节。它最多只能读取16个字节。您需要创建一个单独的方法来始终精确读取16个字节

多次调用
doFinal
不是一个好主意。最好一次读取1024个字节,然后调用
doFinal
,或者-甚至更好的是,
update
。在这种情况下,您应该使用例如
“AES/CBC/NoPadding”
作为算法字符串


注:

  • 如果没有随机IV,您仍然能够区分以相同字节开始的文件(或以相同字节开始的时间重新加密的文件)
  • 您可能需要某种协议来处理小于1024字节的文件
  • 使用“尝试资源”可能是一个非常好的主意
  • 使用文件的内存映射实现更简洁的设计和(可能)更快的操作
首先,SUN提供商将
“AES”
算法字符串在内部转换为
“AES/ECB/PKCS5Padding”
。这意味着您使用的是不安全的ECB模式加密,而不是更安全的CBC模式

获得
BadPaddingException
的原因很简单。使用
PKCS5Padding
(或者更准确地说是PKCS#7 padding),在加密过程中,每16个字节最多填充32个字节。但是,您仅存储未添加的16个字节。如果尝试解密未添加的机制,则会尝试取消添加原始未添加的明文,这将失败

其次,
read
方法实际上可能无法读取16个字节。它最多只能读取16个字节。您需要创建一个单独的方法来始终精确读取16个字节

多次调用
doFinal
不是一个好主意。最好一次读取1024个字节,然后调用
doFinal
,或者-甚至更好的是,
update
。在这种情况下,您应该使用例如
“AES/CBC/NoPadding”
作为算法字符串


注:

  • 如果没有随机IV,您仍然能够区分以相同字节开始的文件(或以相同字节开始的时间重新加密的文件)
  • 您可能需要某种协议来处理小于1024字节的文件
  • 使用“尝试资源”可能是一个非常好的主意
  • 使用文件的内存映射实现更简洁的设计和(可能)更快的操作