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

时间:2016-06-21 13:56:54

标签: java encryption aes

我目前正在开展一个夏季项目,该项目使用AES加密或解密文件。但是,当使用用于加密对象的相同密钥时,在尝试解密时会出现非法块大小异常。我对密码学很陌生,但是一个相当老练的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);
    }
}

最后,我的控制器类,使用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

这不是以任何方式上课,只是一个让我忙碌和新鲜的夏季项目。请帮忙!

1 个答案:

答案 0 :(得分:1)

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