使用 JAVA 使用 AES 加密大文件
Encrypting a large file with AES using JAVA
我用小于此大小(10mb、100mb、500mb)的文件测试了我的代码,并且加密有效。但是,我 运行 遇到了大于 1gb 的文件的问题。
我生成了一个大文件(大约 2gb),我想使用 JAVA 使用 AES 对其进行加密,但我 运行 遇到了这个错误:
"Exception in thread "main" java.lang.OutOfMemoryError: Java 堆 space"
我试过使用 -Xmx8G 增加可用内存,但没有成功。
我的部分代码如下
File selectedFile = new File("Z:\dummy.txt");
Path path = Paths.get(selectedFile.getAbsolutePath());
byte[] toencrypt = Files.readAllBytes(path);
byte[] ciphertext = aesCipherForEncryption.doFinal(toencrypt);
FileOutputStream fos = new FileOutputStream(selectedFile.getAbsolutePath());
fos.write(ciphertext);
fos.close();
据我所知,它这样做的原因是它试图一次读取整个文件,对其进行加密,然后将其存储到另一个字节数组中,而不是将其缓冲和流式传输到. 谁能帮我一些代码提示?
我是编码的初学者,所以我真的不太了解,任何帮助将不胜感激。
甚至不要尝试将整个大文件读入内存。一次加密一个缓冲区。只需使用围绕 FileOutputStream
的适当初始化的 CipherOutputStream
执行标准复制循环。您可以将它用于所有文件,无需对其进行特殊处理。使用 8k 或更大的缓冲区。
EDIT Java中的'standard copy loop'如下:
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
在这种情况下 out = new CipherOutputStream(new FileOutputStream(selectedFile), cipher)
。
您还可以使用我编写的 Encryptor4j 进一步简化该过程:https://github.com/martinwithaar/Encryptor4j
File srcFile = new File("original.zip");
File destFile = new File("original.zip.encrypted");
String password = "mysupersecretpassword";
FileEncryptor fe = new FileEncryptor(password);
fe.encrypt(srcFile, destFile);
本库使用流式加密,因此即使是大文件也不会造成 OutOfMemoryError
。此外,除了使用密码,您还可以使用自己的 Key
。
在此处查看 Github 页面上的示例:https://github.com/martinwithaar/Encryptor4j#file-encryption
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Cypher2021 {
private static final String key = "You're an idiot!";
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";
public static void encrypt(File inputFile) {
File encryptedFile = new File(inputFile.getAbsolutePath() + ".encrypted");
encryptToNewFile(inputFile, encryptedFile);
renameToOldFilename(inputFile, encryptedFile);
}
public static void decrypt(File inputFile) {
File decryptedFile = new File(inputFile.getAbsolutePath() + ".decrypted");
decryptToNewFile(inputFile, decryptedFile);
renameToOldFilename(inputFile, decryptedFile);
}
private static void decryptToNewFile(File input, File output) {
try (FileInputStream inputStream = new FileInputStream(input); FileOutputStream outputStream = new FileOutputStream(output)) {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] buff = new byte[1024];
for (int readBytes = inputStream.read(buff); readBytes > -1; readBytes = inputStream.read(buff)) {
outputStream.write(cipher.update(buff, 0, readBytes));
}
outputStream.write(cipher.doFinal());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void encryptToNewFile(File inputFile, File outputFile) {
try (FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream outputStream = new FileOutputStream(outputFile)) {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] inputBytes = new byte[4096];
for (int n = inputStream.read(inputBytes); n > 0; n = inputStream.read(inputBytes)) {
byte[] outputBytes = cipher.update(inputBytes, 0, n);
outputStream.write(outputBytes);
}
byte[] outputBytes = cipher.doFinal();
outputStream.write(outputBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void renameToOldFilename(File oldFile, File newFile) {
if (oldFile.exists()) {
oldFile.delete();
}
newFile.renameTo(oldFile);
}
}
然后你可以这样使用它:
import java.io.File;
public class Main {
public static void main(String[] args) {
File file = new File("text.txt");
Cypher2021.encrypt(file); // converts "text.txt" into an encrypted file
Cypher2021.decrypt(file); // converts "text.txt" into an decrypted file
}
}
我用小于此大小(10mb、100mb、500mb)的文件测试了我的代码,并且加密有效。但是,我 运行 遇到了大于 1gb 的文件的问题。 我生成了一个大文件(大约 2gb),我想使用 JAVA 使用 AES 对其进行加密,但我 运行 遇到了这个错误:
"Exception in thread "main" java.lang.OutOfMemoryError: Java 堆 space"
我试过使用 -Xmx8G 增加可用内存,但没有成功。 我的部分代码如下
File selectedFile = new File("Z:\dummy.txt");
Path path = Paths.get(selectedFile.getAbsolutePath());
byte[] toencrypt = Files.readAllBytes(path);
byte[] ciphertext = aesCipherForEncryption.doFinal(toencrypt);
FileOutputStream fos = new FileOutputStream(selectedFile.getAbsolutePath());
fos.write(ciphertext);
fos.close();
据我所知,它这样做的原因是它试图一次读取整个文件,对其进行加密,然后将其存储到另一个字节数组中,而不是将其缓冲和流式传输到. 谁能帮我一些代码提示?
我是编码的初学者,所以我真的不太了解,任何帮助将不胜感激。
甚至不要尝试将整个大文件读入内存。一次加密一个缓冲区。只需使用围绕 FileOutputStream
的适当初始化的 CipherOutputStream
执行标准复制循环。您可以将它用于所有文件,无需对其进行特殊处理。使用 8k 或更大的缓冲区。
EDIT Java中的'standard copy loop'如下:
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
在这种情况下 out = new CipherOutputStream(new FileOutputStream(selectedFile), cipher)
。
您还可以使用我编写的 Encryptor4j 进一步简化该过程:https://github.com/martinwithaar/Encryptor4j
File srcFile = new File("original.zip");
File destFile = new File("original.zip.encrypted");
String password = "mysupersecretpassword";
FileEncryptor fe = new FileEncryptor(password);
fe.encrypt(srcFile, destFile);
本库使用流式加密,因此即使是大文件也不会造成 OutOfMemoryError
。此外,除了使用密码,您还可以使用自己的 Key
。
在此处查看 Github 页面上的示例:https://github.com/martinwithaar/Encryptor4j#file-encryption
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Cypher2021 {
private static final String key = "You're an idiot!";
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES";
public static void encrypt(File inputFile) {
File encryptedFile = new File(inputFile.getAbsolutePath() + ".encrypted");
encryptToNewFile(inputFile, encryptedFile);
renameToOldFilename(inputFile, encryptedFile);
}
public static void decrypt(File inputFile) {
File decryptedFile = new File(inputFile.getAbsolutePath() + ".decrypted");
decryptToNewFile(inputFile, decryptedFile);
renameToOldFilename(inputFile, decryptedFile);
}
private static void decryptToNewFile(File input, File output) {
try (FileInputStream inputStream = new FileInputStream(input); FileOutputStream outputStream = new FileOutputStream(output)) {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] buff = new byte[1024];
for (int readBytes = inputStream.read(buff); readBytes > -1; readBytes = inputStream.read(buff)) {
outputStream.write(cipher.update(buff, 0, readBytes));
}
outputStream.write(cipher.doFinal());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void encryptToNewFile(File inputFile, File outputFile) {
try (FileInputStream inputStream = new FileInputStream(inputFile); FileOutputStream outputStream = new FileOutputStream(outputFile)) {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] inputBytes = new byte[4096];
for (int n = inputStream.read(inputBytes); n > 0; n = inputStream.read(inputBytes)) {
byte[] outputBytes = cipher.update(inputBytes, 0, n);
outputStream.write(outputBytes);
}
byte[] outputBytes = cipher.doFinal();
outputStream.write(outputBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void renameToOldFilename(File oldFile, File newFile) {
if (oldFile.exists()) {
oldFile.delete();
}
newFile.renameTo(oldFile);
}
}
然后你可以这样使用它:
import java.io.File;
public class Main {
public static void main(String[] args) {
File file = new File("text.txt");
Cypher2021.encrypt(file); // converts "text.txt" into an encrypted file
Cypher2021.decrypt(file); // converts "text.txt" into an decrypted file
}
}