当前位置: 首页 > 知识库问答 >
问题:

用AES加密和解密大型文件

高化
2023-03-14

我正在尝试用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;
    }

}

共有2个答案

苍宜修
2023-03-14

万一有人感兴趣,我把最后的解决方案放在这里。

它的灵感来自于人们所做的一些评论。主要避免使用字符串,使用byte[]:

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  :-( ");
        }                       
    }

}
屠振濂
2023-03-14

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

您的代码:

   int numBtyes = fis.available();
    byte[] bytes = new byte[numBtyes];
    fis.read(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和CipherInputStream

另一个问题是比较:

String largeFileStringToCheck = new String(largeFileBytesToCheck);

正如已经评论过的,这是一种可怕的内容比较方式。在Java,字符串只用于可打印的字符,当试图“字符串化”任何字节数组时,会应用一种编码,而不可打印的字符可能会被“丢弃”。

对于简单的比较(具有字节数组),可以使用arrays.equals方法

在比较真正大的内容时(当您可能不确定它是否适合您的RAM内存时),通常创建消息哈希并比较哈希是一个好主意

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

 类似资料:
  • 问题内容: 我迅速编写了一个应用程序,我需要AES加密和解密功能,我从另一个.Net解决方案中接收了加密数据,但是我找不到解决办法。 这是我的.net加密: 我需要迅速解密功能。 问题答案: 我找到了解决方案,它是一个很好的库。 跨平台256位AES加密/解密。 此项目包含在所有平台(C#,iOS,Android)上均可使用的256位AES加密的实现。关键目标之一是通过简单的实现使AES在所有平台

  • 我之所以问这个问题,是因为两天来我读了很多关于crypto AES加密的帖子,就在我以为我得到了它的时候,我意识到我根本没有得到它。 这个帖子是最接近我的问题,我有完全相同的问题,但它没有得到回答: CryptoJS AES加密与JAVA AES解密值不匹配 我得到的是已经加密的字符串(我得到的代码只是为了看看他们是怎么做的),所以修改加密方式不是一个选项。这就是为什么所有类似的问题对我来说都不是

  • 问题内容: 我想使用128位AES加密和16字节密钥对密码进行加密和解密。解密值时出现错误。解密时我丢失任何内容吗? 错误信息 最后我基于@QuantumMechanic答案使用以下解决方案 } 问题答案: 如果对于块密码,您将不使用包含填充方案的转换,则需要使明文中的字节数为该密码的块大小的整数倍。 因此,要么将纯文本填充到16字节的倍数(即AES块大小),要么在创建对象时指定填充方案。例如,您

  • 我有这段代码,它基本上对两条纯文本消息进行加密,然后尝试解密,然后打印。问题是第一条消息恢复得很好,但第二条消息是垃圾。我从本教程下载了这段代码,然后将其修改为使用字符串而不是文件,因为我需要它通过套接字发送加密文本。所以其他endpoint不知道明文的长度,有没有办法找到长度,或者我必须以某种方式将明文的长度与密码一起发送? 现在,我认为解密的中断条件有问题。 另外,main()代码在概念上是否

  • 文件解密后,输出包含一些不相关的额外值。为什么结果中有额外的数据?

  • 我试图在Android和PHP端使用AES加密/解密数据,并累犯空答案。 首先,我在Android中生成了对称密钥: 在服务器端,我试图解密数据。我可以解密(从RSA)秘密的AES密钥,并得到它的字符串表示。在客户端(Android)和服务器端(PHP)上是一样的。但是如何使用这个字符串AES密钥来解密数据呢?我尝试了这个(PHP): PHP中的结果: 怎么啦?