java - javax.crypto.BadPaddingException:给定最终块没有正确填充

时间:2016-12-22 15:07:43

标签: java file encryption memory-management aes

我是小reading large file中的bytes chunks。我是encrypting该文件,按块分块,使用AES 128 bit encryptionwriting每个加密块到另一个文件中。该文件为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);
            }
        }
    }
}

2 个答案:

答案 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);

现在解密工作正常。加密也是如此。