【问题标题】:(IllegalBlockSizeException) Input length must be multiple of 16 when decrypting with padded cipher(IllegalBlockSizeException) 使用填充密码解密时,输入长度必须是 16 的倍数
【发布时间】:2016-06-22 04:05:55
【问题描述】:

我目前正在从事一个使用 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

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

【问题讨论】:

  • 查看右侧的相关部分
  • “我只是找不到一个使用文件加密而不是简单文本加密的” 你什么也没做,只是简单的文本加密,即使您正在使用byte[] 阅读它。所以不要忽视这些重复的问题。
  • 这看起来像是一个合适的副本:stackoverflow.com/questions/17234359/…。尝试第一个答案的建议(得分最高的答案)
  • 一般建议:始终使用完全限定的密码字符串。 Cipher.getInstance("AES"); 可能会产生不同的密码,具体取决于默认的安全提供程序。它最有可能导致"AES/ECB/PKCS5Padding",但并非必须如此。如果它发生变化,您将失去不同 JVM 之间的兼容性。
  • 切勿使用ECB mode。它是确定性的,因此在语义上不安全。您至少应该使用像CBCCTR 这样的随机模式。最好对您的密文进行身份验证,以免像padding oracle attack 这样的攻击是不可能的。这可以通过 GCM 或 EAX 等认证模式或encrypt-then-MAC 方案来完成。

标签: java encryption aes


【解决方案1】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 2013-06-18
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-14
    相关资源
    最近更新 更多