java - (IllegalBlockSizeException) 使用填充密码解密时,输入长度必须是 16 的倍数

标签 java encryption aes

我目前正在开展一个夏季项目,该项目使用 AES 加密或解密文件。但是,当使用与加密对象相同的 key 时,我在尝试解密时收到非法 block 大小异常。我对密码学很陌生,但也是一位经验丰富的 Java 程序员。

密码学家类别:

package com.spencergang.fem;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class Cryptographer {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES";

    //Method for cryptography
    public static void crypto(int cipherMode, String key, File inputFile, File outputFile) throws CryptoException {
        try{
            Key secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(cipherMode, secretKey);

            FileInputStream inputStream = new FileInputStream(inputFile);
            byte[] inputBytes = new byte[(int) inputFile.length()];
            inputStream.read(inputBytes);

            byte[] outputBytes = cipher.doFinal(inputBytes);

            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(outputBytes);

            inputStream.close();
            outputStream.close();
        }catch(NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException ex){
            throw new CryptoException("ERROR ENCRYPTING / DECRYPTING FILE", ex);
        }
    }

    //Encryption
    public static void encrypt(String key, File inputFile, File outputFile) throws CryptoException {
        crypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
    }

    //Decryption
    public static void decrypt(String key, File inputFile, File outputFile) throws CryptoException {
        crypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
    }
}

CryptoException 类:

package com.spencergang.fem;

public class CryptoException extends Exception {
    public CryptoException(){
    }

    public CryptoException(String message, Throwable throwable){
        super(message, throwable);
    }
}

最后,我的 Controller 类,使用 FXML 创建了一个非常漂亮的 GUI:

package com.spencergang.fem;

import java.io.File;

import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;

public class Controller {
    @FXML
    private TextField path;
    @FXML
    private TextField destination;
    @FXML
    private TextField key;
    @FXML
    private CheckBox encryption;
    @FXML
    private CheckBox decryption;
    @FXML
    private Button begin;
    @FXML 
    private Button browse;

    private MainApp mainApp;

    private Cryptographer crypto;

    String selectedKey;
    File input;
    File output;
    String filePath;
    String fileName;
    String destinationPath;
    String destinationFile;
    String finalDestinationPath;

    //Default Constructor;
    public Controller(){
    }

    @FXML
    private void initalize(){
    }

    public void setMainApp(MainApp mainApp){
        this.mainApp = mainApp;
    }

////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////ENCRYPTION AND DECRYPTION METHODS/////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////

    private void handleEncryption(String key, File inputFile, File outputFile) throws CryptoException{
        crypto.encrypt(key, inputFile, outputFile);
    }

    private void handleDecryption(String key, File inputFile, File outputFile) throws CryptoException{
        crypto.decrypt(key, inputFile, outputFile);
    }

////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////BUTTON HANDLER METHODS////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////    

    @FXML
    private void handleBegin() throws CryptoException{

        if(!(destination.getText().trim().isEmpty() || destination.getText() == "" || destination.getText() == null)){
            finalDestinationPath = destinationPath + destination.getText();
            output = new File(finalDestinationPath);
        }

        if(checkFields() == true){
            selectedKey = key.getText();
        }else{
            fieldAlert();
        }

        if(checkBoxes() == 1){
            handleEncryption(selectedKey, input, output);
        }else if(checkBoxes() == 2){
            handleDecryption(selectedKey, input, output);
        }else if(checkBoxes() == 3){
            doubleCheckAlert();
        }else if(checkBoxes() == 4){
            noCheckAlert();
        }
    }

    @FXML
    private void handleBrowse(){
        FileChooser fileChooser = new FileChooser();
        File file;
        fileChooser.setTitle("Select File");
        file = fileChooser.showOpenDialog(mainApp.getPrimaryStage());
        filePath = file.getAbsolutePath();
        fileName = file.getName();
        System.out.println(fileName);
        path.setText(filePath);
        destinationPath = filePath.replace(fileName, "");
        input = file;
    }

////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////HELPER METHODS////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////

    private boolean checkFields(){
        if(path.getText().trim().isEmpty() || destination.getText().trim().isEmpty() || key.getText().trim().isEmpty() || destination.getText() == "" || key.getText() == "" || path.getText() == null || destination.getText() == null || key.getText() == null){
            return false;
        }else{
            return true;
        }
    }

    private int checkBoxes(){
        if(encryption.isSelected() && decryption.isSelected()){
            return 3;
        }else if(decryption.isSelected()){
            return 2;
        }else if(encryption.isSelected()){
            return 1;
        }else{
            return 4;
        }
    }

////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////ERROR AND CONFIRMATION ALERTS/////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////

    private void encryptionAlert(){
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.initOwner(mainApp.getPrimaryStage());
        alert.setTitle("Encryption Successful");
        alert.setHeaderText("File Encrypted");
        alert.setContentText("Your file has been successfully encrypted!");
        alert.showAndWait();
    }

    private void fieldAlert(){
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.initOwner(mainApp.getPrimaryStage());
        alert.setTitle("ERROR");
        alert.setHeaderText("Empty Field(s)");
        alert.setContentText("Check that you have filled out all fields");
        alert.showAndWait();
    }

    private void decryptionAlert(){
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.initOwner(mainApp.getPrimaryStage());
        alert.setTitle("Decryption Successful");
        alert.setHeaderText("File Decrypted");
        alert.setContentText("Your file has been successfully decrypted!");
        alert.showAndWait();
    }

    private void failureAlert(){
        Alert alert = new Alert(AlertType.ERROR);
        alert.initOwner(mainApp.getPrimaryStage());
        alert.setTitle("ERROR");
        alert.setHeaderText("File Not Found");
        alert.setContentText("Please check your path values");
        alert.showAndWait();
    }

    private void doubleCheckAlert(){
        Alert alert = new Alert(AlertType.ERROR);
        alert.initOwner(mainApp.getPrimaryStage());
        alert.setTitle("ERROR");
        alert.setHeaderText("Selection Error");
        alert.setContentText("Please select only one option");
        alert.showAndWait();
    }

    private void noCheckAlert(){
        Alert alert = new Alert(AlertType.ERROR);
        alert.initOwner(mainApp.getPrimaryStage());
        alert.setTitle("ERROR");
        alert.setHeaderText("Selection Error");
        alert.setContentText("Please select an option");
        alert.showAndWait();
    }
}

错误消息:

Caused by: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:913)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at com.spencergang.fem.Cryptographer.crypto(Cryptographer.java:32)
    ... 61 more

这绝不是为了上课,只是一个让我保持忙碌和新鲜的夏季项目。请帮忙!

最佳答案

加密数据,然后不将其写入文件,立即解密字节。那样有用吗?或者,写入后立即读取字节并与原始字节进行比较。他们匹配吗?问题可能是您的 FileOutputStream 以某种字符编码而不是二进制形式将字节写入文件。在这种情况下,非法序列(在给定的编码中)可能会在传输到文件的过程中被修改,从而意外地更改您的密文。

关于java - (IllegalBlockSizeException) 使用填充密码解密时,输入长度必须是 16 的倍数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37946487/

相关文章:

c# - .NET 加密字节数组

cocoa - 实现与加密一起使用的文件格式 - Cocoa

c# - REST API token 认证

java - 通过 HTTP 将大型视频文件从 Tomcat 服务器传输到 Android 客户端

java - 我的游戏循环不绘制生成对象

java - 以编程方式检索 Internet Explorer 缓存的代理用户名

java - 在 Java 套接字之上编写协议(protocol)的问题

python-2.7 - 如何使用 Boto3 加密我现有的 RDS 实例?

objective-c - NSData 到 NSString,反之亦然

java - Java中如何使用newAPIHadoopRDD(spark)读取Hbase数据