Java 使用AES加密和解密大型文件

Java 使用AES加密和解密大型文件,java,encryption,aes,Java,Encryption,Aes,我试图用AES加密一个大文件,然后解密并与原始文件进行比较 这节课总结了这项工作。它适用于.txt文件,但不适用于.mp3、.pdf等文件 非常感谢您的帮助 import java.io.FileInputStream; import java.io.FileOutputStream; public class LargeFileEncryptionTest7 { protected static String FOLDER_PATH = "C:/temp/"; protec

我试图用AES加密一个大文件,然后解密并与原始文件进行比较

这节课总结了这项工作。它适用于.txt文件,但不适用于.mp3、.pdf等文件

非常感谢您的帮助

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class LargeFileEncryptionTest7 {

    protected static String FOLDER_PATH = "C:/temp/";
    protected static String FILE = "some-large-file";
    protected static String EXT = ".mp3"; //Works for .txt, but not for .mp3 or .pdf

    public static void main(String[] args) throws Exception {

        //Load file to encrypt
        byte[] largeFileBytes = loadFile(FOLDER_PATH + FILE + EXT);
        String largeFileString = new String(largeFileBytes);

        //Encrypt file with AES
        AESUtils aesUtils = new AESUtils();
        byte[] secretKey = aesUtils.generateSecretKey();
        aesUtils.setSecretKey(secretKey);
        byte[] largeFileEncBytes = aesUtils.encrypt(largeFileString);

        //Save encrypted file
        saveFile(largeFileEncBytes, FOLDER_PATH + FILE + "-encrypted" + EXT);

        //Load encrypted file
        byte[] largeFileEncBytesToCheck = loadFile(FOLDER_PATH + FILE + "-encrypted" + EXT);

        //Decrypt file      
        byte[] largeFileBytesToCheck = aesUtils.decrypt(largeFileEncBytesToCheck);
        String largeFileStringToCheck = new String(largeFileBytesToCheck);

        //Save decrypted file
        saveFile(largeFileBytesToCheck, FOLDER_PATH + FILE + "-decrypted" + EXT);

        //Check strings
        //System.out.println("Original content: " + largeFileStringToCheck);
        if (largeFileStringToCheck.equals(largeFileString)) {
            System.out.println("OK  :-) ");
        } else {
            System.out.println("KO  :-( ");
        }                       
    }

    private static void saveFile(byte[] bytes, String fileName) throws Exception {
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(bytes);
        fos.close();
    }

    private static byte[] loadFile(String fileName) throws Exception {
        FileInputStream fis = new FileInputStream(fileName);
        int numBtyes = fis.available();
        byte[] bytes = new byte[numBtyes];
        fis.read(bytes);
        fis.close();
        return bytes;
    }

}

我发现您的解决方案存在两个问题:

您的代码:

这实际上并不能保证整个内容都被阅读。同样,当加密大文件时,如果无法保证它能装入内存,那么您可能不希望将所有内容读入内存

当加密/解密大型内容时,您可能希望使用以下方法:

byte[] buff = new byte[BUFFERSIZE];
for(int readBytes=in.read(buff); readBytes>-1;readBytes=in.read(buff)) {
  out.write(cipher.update(buff,0, readBytes);
}
out.write(cipher.doFinal());
或者看看CipherOutputStream和

另一个问题是比较:

如前所述,这是一种比较内容的糟糕方式。在Java中,字符串仅用于可打印字符,当尝试对任何字节数组进行字符串化时,会应用编码,不可打印字符可能会被丢弃

对于具有字节数组的简单比较,可以使用

当比较非常大的内容时,当您可能不确定它是否适合RAM内存时,通常最好创建一个比较哈希的方法


编辑:如果您确实想查看/打印/比较作为字符串的密文,您可以对二进制数据进行编码,您可以查看。

我发现您的解决方案存在两个问题:

您的代码:

这实际上并不能保证整个内容都被阅读。同样,当加密大文件时,如果无法保证它能装入内存,那么您可能不希望将所有内容读入内存

当加密/解密大型内容时,您可能希望使用以下方法:

byte[] buff = new byte[BUFFERSIZE];
for(int readBytes=in.read(buff); readBytes>-1;readBytes=in.read(buff)) {
  out.write(cipher.update(buff,0, readBytes);
}
out.write(cipher.doFinal());
或者看看CipherOutputStream和

另一个问题是比较:

如前所述,这是一种比较内容的糟糕方式。在Java中,字符串仅用于可打印字符,当尝试对任何字节数组进行字符串化时,会应用编码,不可打印字符可能会被丢弃

对于具有字节数组的简单比较,可以使用

当比较非常大的内容时,当您可能不确定它是否适合RAM内存时,通常最好创建一个比较哈希的方法


编辑:如果你真的想看到/打印/比较作为字符串的密文,你可以对二进制数据进行编码,你可以看看。

如果有人感兴趣,我把最终的解决方案放在这里

人们所做的一些评论启发了这一点。主要避免使用字符串并使用字节[]:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class LargeFileEncryptionTest11 {

    private static final String FOLDER_PATH = "C:/temp/";
    private static final String FILE = "some-large-file";
    private static final String EXT = ".pdf";

    private static final String ENCRYPTION_ALGORITHM = "AES";
    private static final int KEY_SIZE = 128; // 192 and 256 bits may not be available

    public static void main(String[] args) throws Exception {
        //Common stuff to encrypt/decrypt
        KeyGenerator kgen = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM);
        kgen.init(KEY_SIZE); 
        SecretKey skey = kgen.generateKey();
        byte[] secretKey = skey.getEncoded();
        SecretKeySpec skeySpec = new SecretKeySpec(secretKey, ENCRYPTION_ALGORITHM);
        Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);

        //Load file to encrypt
        byte[] largeFileBytes = Files.readAllBytes(Paths.get(FOLDER_PATH + FILE + EXT));

        //Encrypt file
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] largeFileEncBytes = cipher.doFinal(largeFileBytes);

        //Save encrypted file
        Files.write(Paths.get(FOLDER_PATH + FILE + "-encrypted" + EXT), largeFileEncBytes);

        //Load encrypted file
        byte[] largeFileEncBytesToCheck = Files.readAllBytes(Paths.get(FOLDER_PATH + FILE + "-encrypted" + EXT));

        //Decrypt file      
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] largeFileBytesToCheck = cipher.doFinal(largeFileEncBytesToCheck);

        //Save decrypted file
        Files.write(Paths.get(FOLDER_PATH + FILE + "-decrypted" + EXT), largeFileBytesToCheck);

        //Compare results
        if (Arrays.equals(largeFileBytes, largeFileBytesToCheck)) {
            System.out.println("OK  :-) ");
        } else {
            System.out.println("KO  :-( ");
        }                       
    }

}

如果有人感兴趣,我在这里提出最终解决方案

人们所做的一些评论启发了这一点。主要避免使用字符串并使用字节[]:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class LargeFileEncryptionTest11 {

    private static final String FOLDER_PATH = "C:/temp/";
    private static final String FILE = "some-large-file";
    private static final String EXT = ".pdf";

    private static final String ENCRYPTION_ALGORITHM = "AES";
    private static final int KEY_SIZE = 128; // 192 and 256 bits may not be available

    public static void main(String[] args) throws Exception {
        //Common stuff to encrypt/decrypt
        KeyGenerator kgen = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM);
        kgen.init(KEY_SIZE); 
        SecretKey skey = kgen.generateKey();
        byte[] secretKey = skey.getEncoded();
        SecretKeySpec skeySpec = new SecretKeySpec(secretKey, ENCRYPTION_ALGORITHM);
        Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);

        //Load file to encrypt
        byte[] largeFileBytes = Files.readAllBytes(Paths.get(FOLDER_PATH + FILE + EXT));

        //Encrypt file
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] largeFileEncBytes = cipher.doFinal(largeFileBytes);

        //Save encrypted file
        Files.write(Paths.get(FOLDER_PATH + FILE + "-encrypted" + EXT), largeFileEncBytes);

        //Load encrypted file
        byte[] largeFileEncBytesToCheck = Files.readAllBytes(Paths.get(FOLDER_PATH + FILE + "-encrypted" + EXT));

        //Decrypt file      
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] largeFileBytesToCheck = cipher.doFinal(largeFileEncBytesToCheck);

        //Save decrypted file
        Files.write(Paths.get(FOLDER_PATH + FILE + "-decrypted" + EXT), largeFileBytesToCheck);

        //Compare results
        if (Arrays.equals(largeFileBytes, largeFileBytesToCheck)) {
            System.out.println("OK  :-) ");
        } else {
            System.out.println("KO  :-( ");
        }                       
    }

}

你应该把编码排除在外。要么在任何地方都明确提到编码,要么不再使用旧的繁琐的文件api,而是使用NIO。例如,将loadFile方法替换为Files.readAllBytesPath.getfileName,就是这样。它将始终使用UTF-8。与saveFile类似,只有Files.writePaths.getfileName,bytes,done。在创建字符串时还要提到编码新的Stringbytes,StandardCharset.UTF_8。只要确保每个地方的编码都相同,否则就会遇到问题。最后,不要比较字符串。比较原始数据,字节[]s,否则您将再次依赖编码。如果字节仍然相等,则加密/解密进行得很顺利,只是您没有为字符串表示指定正确的编码。尝试将任意二进制数据转换为字符串似乎不是一个好主意。AESUtils是否有接受字节[]作为输入的方法?如果没有,你能加一个吗?就像迈克尔说的。坏主意是轻描淡写。任何承诺对字符串执行加密的lib/util要么注定会让您失望,例如,由一个没有线索的人提供,要么通过阅读文档来做出您应该知道的假设,例如,它仅在字符串输入Base64编码字符串时才起作用。加密是字节级的过程,而不是字符级的过程。再次强调前一点,在不指定编码的情况下将任何二进制文件转换为字符串会导致数据损坏,并且有一天会崩溃。不要。你应该把编码排除在外。要么在任何地方都明确提到编码,要么不再使用旧的繁琐的文件api,而是使用NIO。例如,将loadFile方法替换为Files.readAllBytesPath.getfileName,就是这样。它将始终使用UTF-8。与saveFile类似,只有Files.writePaths.getfileName,bytes,done。在创建字符串时还要提到编码新的Stringbytes,StandardCharset.UTF_8。只要确保每个地方的编码都相同,否则就会遇到问题。最后,不要比较字符串。比较原始数据,字节[]s,否则您将再次依赖编码。如果字节仍然相等,则加密/解密失败
只是您没有为字符串表示指定正确的编码。尝试将任意二进制数据转换为字符串似乎是个坏主意。AESUtils是否有接受字节[]作为输入的方法?如果没有,你能加一个吗?就像迈克尔说的。坏主意是轻描淡写。任何承诺对字符串执行加密的lib/util要么注定会让您失望,例如,由一个没有线索的人提供,要么通过阅读文档来做出您应该知道的假设,例如,它仅在字符串输入Base64编码字符串时才起作用。加密是字节级的过程,而不是字符级的过程。再次强调前一点,在不指定编码的情况下将任何二进制文件转换为字符串会导致数据损坏,并且有一天会崩溃。这个解决方案的一个主要缺点是它需要至少两倍于RAM中的文件大小。因此,考虑到问题的标题“大”可能意味着几GB或更多,这个解决方案设计得不好。对于我们的项目,我认为它可以工作。我们只需要加密最多15分钟的mp3录音。这种解决方案的主要缺点是它需要至少两倍于RAM中文件大小。因此,考虑到问题的标题“大”可能意味着几GB或更多,这个解决方案设计得不好。对于我们的项目,我认为它可以工作。我们只需要加密最多15分钟的mp3录音。