我是小reading
large file
中的bytes
chunks
。我是encrypting
该文件,按块分块,使用AES 128 bit encryption
和writing
每个加密块到另一个文件中。该文件为Encrypted
successfully
。
但当我reading
encrypted
文件回到小 - bytes
chunks
并尝试decrypt
那个文件块时,它抛出异常
java - javax.crypto.BadPaddingException: Given final block not properly padded
但是当我尝试读取整个文件并尝试decrypt
个字节时,它decryptes
成功。
这是我的代码:
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* Created by Ashish Pancholi on 22-12-2016.
*/
public class EncryDecryPtion implements Securable{
public static void main(String[] argu){
EncryDecryPtion encryDecryPtion = new EncryDecryPtion();
File file = new File("shouldbeoriginal.jpg");
try {
encryDecryPtion.encryptFile(file,"Pa$$w0rd");
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
File file_ = new File("shouldbeoriginal.jpg");
try {
encryDecryPtion.decryptFile(file_,"Pa$$w0rd");
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
}
}
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* Encrypt and decrypt file with AES algorithm
* Created by Ashish Pancholi on 20-12-2016.
*/
public interface Securable {
/**
* Read and write the file in chunk.
* Encrypts the chunks with AES algorithm.
* It creates a new a file which having encrypted data,
* deletes old original file and
* rename a new file with the old file
* @param file which is to be encrypted and password.
*/
default File encryptFile(File file, String password) throws IOException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
AESEncryptionDecryption aesEncryptionDecryption = new AESEncryptionDecryption(password);
String encryptedFilePath = file.getAbsolutePath() + ".ENCRYPTED";
File encryptedFile = new File(encryptedFilePath);
encryptedFile.createNewFile();
try
(FileInputStream in = new FileInputStream(file)) {
try
(OutputStream out = new FileOutputStream(encryptedFile)) {
byte[] chunk = new byte[1024];
int chunkLen = 0;
while ((chunkLen = in.read(chunk)) != -1) {
byte[] encryptedChunk = aesEncryptionDecryption.encrypt(chunk);
out.write(encryptedChunk);
}
}
}
Path path_originalFile = Paths.get(file.getAbsolutePath());
Path path_encryptedFile = Paths.get(encryptedFile.getAbsolutePath());
try {
Files.delete(path_originalFile);
}catch (IOException ex){
try {
FileUtils.forceDelete(file);
}catch (IOException ex1){
//ignore
}
}
Path path = Files.move(path_encryptedFile, path_originalFile);
return path.toFile();
}
default File encryptWholeFile(File file, String password) throws IOException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
AESEncryptionDecryption aesEncryptionDecryption = new AESEncryptionDecryption(password);
String encryptedFilePath = file.getAbsolutePath() + ".ENCRYPTED";
File encryptedFile = new File(encryptedFilePath);
encryptedFile.createNewFile();
try(FileInputStream in = new FileInputStream(file)) {
byte[] bytes = IOUtils.toByteArray(in);
byte[] encryptedChunk = aesEncryptionDecryption.encrypt(bytes);
FileUtils.writeByteArrayToFile(encryptedFile, encryptedChunk);
}
Path path_originalFile = Paths.get(file.getAbsolutePath());
Path path_encryptedFile = Paths.get(encryptedFile.getAbsolutePath());
try {
Files.delete(path_originalFile);
}catch (IOException ex){
try {
FileUtils.forceDelete(file);
}catch (IOException ex1){
//ignore
}
}
Path path = Files.move(path_encryptedFile, path_originalFile);
return path.toFile();
}
/**
* Read and write the file in chunk.
* Encrypts the chunks with AES algorithm.
* It creates a new a file which having encrypted data,
* deletes old original file and
* rename a new file with the old file
* @param inputStream of file which is to be encrypted and a password.
*/
default InputStream encryptFile(InputStream inputStream, String password) throws IOException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
InputStream in;
try {
AESEncryptionDecryption aesEncryptionDecryption = new AESEncryptionDecryption(password);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] chunk = new byte[1024];
int chunkLen = 0;
while ((chunkLen = inputStream.read(chunk)) != -1) {
byte[] encryptedChunk = aesEncryptionDecryption.encrypt(chunk);
baos.write(encryptedChunk);
}
baos.flush();
in = new ByteArrayInputStream(baos.toByteArray());
}
}finally {
inputStream.close();
}
return in;
}
/**
* Read and write the file in chunk.
* Encrypts the chunks with AES algorithm.
* It creates a new a file which having encrypted data,
* deletes old original file and
* rename a new file with the old file
* @param inputStream of file which is to be encrypted and a password.
*/
default File encryptFile(InputStream inputStream, String password, String targetFileName) throws IOException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
File encryptedFile = new File(targetFileName);
try {
AESEncryptionDecryption aesEncryptionDecryption = new AESEncryptionDecryption(password);
encryptedFile.getParentFile().mkdirs();
encryptedFile.createNewFile();
try (OutputStream baos = new FileOutputStream(encryptedFile)) {
byte[] chunk = new byte[1024];
int chunkLen = 0;
while ((chunkLen = inputStream.read(chunk)) != -1) {
byte[] encryptedChunk = aesEncryptionDecryption.encrypt(chunk);
baos.write(encryptedChunk);
}
}
}finally {
inputStream.close();
}
return encryptedFile;
}
/**
* Read and write the file in chunk.
* Decrypts the chunks with AES algorithm.
* It creates a new a file which having decrypted data,
* deletes old original encrypted file and
* rename a new file with the old file
* @param file which is to be decrypted and password.
*/
default void decryptFile(File file, String password) throws IOException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
AESEncryptionDecryption aesEncryptionDecryption = new AESEncryptionDecryption(password);
String decryptedFilePath = file.getAbsolutePath() + ".DECRYPTED";
File decryptedFile = new File(decryptedFilePath);
decryptedFile.createNewFile();
try
(FileInputStream in = new FileInputStream(file)) {
try
(OutputStream out = new FileOutputStream(decryptedFile)) {
byte[] chunk = new byte[1024];
int chunkLen = 0;
while ((chunkLen = in.read(chunk)) != -1) {
byte[] encryptedChunk = aesEncryptionDecryption.decrypt(chunk);
out.write(encryptedChunk);
}
}
}
Path path_originalFile = Paths.get(file.getAbsolutePath());
Path path_decryptedFile = Paths.get(decryptedFile.getAbsolutePath());
try {
Files.delete(path_originalFile);
}catch (IOException ex){
try {
FileUtils.forceDelete(file);
}catch (IOException ex1){
//ignore
}
}
Files.move(path_decryptedFile, path_originalFile);
}
default File decryptWholeFile(File file, String password) throws IOException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException {
AESEncryptionDecryption aesEncryptionDecryption = new AESEncryptionDecryption(password);
String decryptedFilePath = file.getAbsolutePath() + ".DECRYPTED";
File decryptedFile = new File(decryptedFilePath);
decryptedFile.createNewFile();
try(FileInputStream in = new FileInputStream(file)) {
byte[] bytes = IOUtils.toByteArray(in);
byte[] encryptedChunk = aesEncryptionDecryption.decrypt(bytes);
FileUtils.writeByteArrayToFile(decryptedFile, encryptedChunk);
}
Path path_originalFile = Paths.get(file.getAbsolutePath());
Path path_decryptedFile = Paths.get(decryptedFile.getAbsolutePath());
try {
Files.delete(path_originalFile);
}catch (IOException ex){
try {
FileUtils.forceDelete(file);
}catch (IOException ex1){
//ignore
}
}
Path path = Files.move(path_decryptedFile, path_originalFile);
return path.toFile();
}
}
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* Encrypt and decrypt file with AES algorithm
* Created by Ashish Pancholi on 20-12-2016.
*/
public class AESEncryptionDecryption {
private SecretKeySpec secretKey;
private byte[] key;
public AESEncryptionDecryption(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException{
MessageDigest sha = null;
key = password.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
this.secretKey = new SecretKeySpec(key, "AES");
}
/**
* Encrypts the file with AES algorithm
* @param bytes of file which is to encrypted
* @return byte[] which is encrypted bytes
*/
public byte[] encrypt(byte[] bytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, this.secretKey);
byte[] encrytedBytes = cipher.doFinal(bytes);
return encrytedBytes;
}
/**
* Decrypts the file with AES algorithm
* @param encrytedBytes of file that to be decrypted
* @return byte[] which is original data.
*/
public byte[] decrypt(byte[] encrytedBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
byte[] bytes = cipher.doFinal(encrytedBytes);
return bytes;
}
}
所以我的问题是 - 如何加密大文件而不将整个文件加载到内存中?请帮忙。
EDITED
以下是更新的代码,但在解密时我仍然会在cipher.doFinal()
行上获得相同的例外:
public void encrypt(File sourceFile, File targetFile) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, this.secretKey);
try(InputStream inputStream = new FileInputStream(sourceFile)){
try(OutputStream outputStream = new FileOutputStream(targetFile)){
byte[] chunk = new byte[8192];
int chunkLen = 0;
while ((chunkLen = inputStream.read(chunk)) != -1) {
byte[] encrytedBytes = cipher.update(chunk);
outputStream.write(encrytedBytes);
}
byte[] finalBytes = cipher.doFinal();
if(finalBytes!=null) {
outputStream.write(finalBytes);
}
}
}
}
public void decrypt(File encryptedFile, File targetFile) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
try(InputStream inputStream = new FileInputStream(encryptedFile)){
try(OutputStream outputStream = new FileOutputStream(targetFile)){
byte[] chunk = new byte[8192];
int chunkLen = 0;
while ((chunkLen = inputStream.read(chunk)) != -1) {
byte[] decrytedBytes = cipher.update(chunk);
outputStream.write(decrytedBytes);
}
byte[] finalBytes = cipher.doFinal();
if(finalBytes!=null) {
outputStream.write(finalBytes);
}
}
}
}
答案 0 :(得分:3)
在流式传输文件时应使用cipher.update(...)
方法,并且仅使用cipher.doFinal(...)
作为最后一次调用。 doFinal
刷新缓冲区+执行填充等,您不想做多次。
如果您过早地在解密中执行doFinal(...)
,则很可能会失败,因为数据中缺少正确的填充。
编辑:
不要使用ECB模式,这是不安全的。看看here并向下滚动到企鹅。
不要通过简单的Sha-1生成密钥 - 使用正确的密钥派生函数,如PBKDF2WithHmacSHA256。
答案 1 :(得分:0)
最初,我在加密和解密模式下都使用cipher.doFinal()
。运行时Exceution很好,但是测试用例因BadPaddingException()
而失败。然后我将代码更改为
byte[] output = this.cipher.update(input, 0, input.length);
现在解密工作正常。加密也是如此。