在 Java 中实现 IllegalBlockSizeException 的解决方案?

Implementing solution to IllegalBlockSizeException in Java?

我正在尝试修复我的代码。在我的代码中,我试图生成一个我已经完成的 16 位密钥。其次,生成一条随机消息,这也已完成。加密和解密我收到错误的数据。最后有一个蛮力算法来解密我稍后会尝试做的消息。因此,对于我的加密,代码对其进行加密但不对随机生成的字符串进行加密。我遇到了一堆错误。

我的代码:

import java.util.Random;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Assignment1Demo {

private static String msg;

private static String msgE;

private static String msgD;

private static int key;


public static void main(String[] args){

//TODO: You can only call methods in main method

key = generateKey();

msg = generateMsg();

msgE = encryption(key,msg);

bruteForce(msgE);

}

private static int generateKey() {

//TODO: implement step a (randomly generate 16-bit key)

//16 bit digit means 2^16 -1 in decimal

Random rand = new Random();

return rand.nextInt((int) (Math.pow(2, 16)-1));

}

private static String generateMsg() {

//TODO: implement step b (randonly generate a string with an even number of characters)
    String chractersU="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    String chractersL=chractersU.toLowerCase();
    String space=" ";
    String alphanum=chractersU+space+chractersL;
    String random="";
    int length=alphanum.length();
    Random rand=new Random();
    char[] text=new char[length];

    for(int i=0;i<length;i++) {
        text[i]=alphanum.charAt(rand.nextInt(alphanum.length()));
    }


for(int i=0;i<text.length/2;i++) {
    if(text.length%2!=0) {
        random += text[i];
    }}


return random;

}

private static String encryption (int key, String msg) {

//TODO: implement step c (encrypt the message)

String strData="";
String strKey=Integer.toString(key);

    try {
        SecretKeySpec skeyspec=new SecretKeySpec(strKey.getBytes(),"Blowfish");
        Cipher cipher=Cipher.getInstance("Blowfish");
        cipher.init(Cipher.ENCRYPT_MODE, skeyspec);
        byte[] encrypted=cipher.doFinal(msg.getBytes());
        strData=new String(encrypted);

    } catch (Exception e) {
        e.printStackTrace();

    }
    return strData;
}






private static void decryption(int key, String msgE) {

//TODO: implement step d (decryption)

String strKey = Integer.toString(key);
String strData="";

try {
    SecretKeySpec skeyspec=new SecretKeySpec(strKey.getBytes(),"Blowfish");
    Cipher cipher=Cipher.getInstance("Blowfish");
    cipher.init(Cipher.DECRYPT_MODE, skeyspec);
    byte[] decrypted=cipher.doFinal(msgE.getBytes());
    strData=new String(decrypted);

} catch (Exception e) {
    e.printStackTrace();

}
System.out.println(strData);
}

private static void bruteForce(String msgE) {

//TODO: implement bruteForce algorithm, you may need the above decryption(key,msgE) method

boolean isEnglisString = msgE.matches("[a-zA-Z]+");

if(isEnglisString)

System.out.println("Yes encrypted message is Randomly English generated message " + msgE);

else
System.out.println("encrypted message is Not Randomly english generated message "+msgE);
decryption(key, msgE);
isEnglisString = msgD.matches("[a-zA-Z]+");
if(isEnglisString)
System.out.println("Yes decrypted message is Randomly english generated message "+ msgD);
else
System.out.println("decrypted message is not Randomly english generated message "+ msgD);
}}

错误:

javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
    at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:936)
    at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
    at java.base/com.sun.crypto.provider.BlowfishCipher.engineDoFinal(BlowfishCipher.java:319)
    at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2189)
    at Assignment1Demo.decryption(Assignment1Demo.java:110)
    at Assignment1Demo.bruteForce(Assignment1Demo.java:132)
    at Assignment1Demo.main(Assignment1Demo.java:30)

Exception in thread "main" java.lang.NullPointerException
    at Assignment1Demo.bruteForce(Assignment1Demo.java:133)
    at Assignment1Demo.main(Assignment1Demo.java:30)

您的字符串是随机生成的,但是当块大小与密码预期的不匹配时,您需要应用可接受的填充算法,以便输入的大小与算法保持一致。这适用于所有分组密码。

这个例子是一个简单的密码计,而不是可以使用类似算法的东西 "AES/CBC/PKCS5Padding" "RC2/CBC/PKCS5Padding" “DESede/CBC/PKCS5Padding (PKCS#5 填充是为 8 字节块大小定义的)

@Override
    public SimpleMeter testEncryption(File baseInput, String algorithm, String provider) throws NoSuchProviderException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        Cipher cipherEncryption;

        SimpleMeter meter = new SimpleMeter();

        SecureRandom randGenerator = new SecureRandom();
        KeyGenerator generator;

        generator = KeyGenerator.getInstance(algorithm.split("/")[0], provider);
        generator.init(randGenerator);
        SecretKey key = generator.generateKey();

        cipherEncryption = Cipher.getInstance(algorithm, provider);
        cipherEncryption.init(Cipher.ENCRYPT_MODE, key);

        try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(baseInput))) {

            CipherInputStream encryptionStream = new CipherInputStream(input, cipherEncryption);

            meter.start();

            while (encryptionStream.read() > -1);

            encryptionStream.close();//End all encryption and decryption operation

            meter.stop();
        } catch (Exception ex) {
            ex.printStackTrace();
            meter = null;
        }

        return meter;
    }

另一个没有流的例子:

cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(encryptedString.getBytes(CHARSET_ISO_8859_1));
 decryptedString = new String(decrypted, CHARSET_ISO_8859_1);

确实没有必要,但是如果你想用base64,你可以找Apache: http://commons.apache.org/proper/commons-codec/


使用您的代码的示例

1) 欧洲央行模式

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Random;

public class Assignment1Demo {

    private static String msg;

    private static byte[] msgE;

    private static String msgD;

    private static int key;

    public static void main( String[] args ) {

        //TODO: You can only call methods in main method

        key = generateKey( );

        msg = generateMsg( );

        msgE = encryption( key, msg );

        bruteForce( msgE );

    }

    private static int generateKey( ) {

        //TODO: implement step a (randomly generate 16-bit key)

        //16 bit digit means 2^16 -1 in decimal

        Random rand = new Random( );

        return rand.nextInt( ( int ) ( Math.pow( 2, 16 ) - 1 ) );

    }

    private static String generateMsg( ) {

        //TODO: implement step b (randonly generate a string with an even number of characters)
        String chractersU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String chractersL = chractersU.toLowerCase( );
        String space = " ";
        String alphanum = chractersU + space + chractersL;
        String random = "";
        int length = alphanum.length( );
        Random rand = new Random( );
        char[] text = new char[ length ];

        for ( int i = 0; i < length; i++ ) {
            text[ i ] = alphanum.charAt( rand.nextInt( alphanum.length( ) ) );
        }

        for ( int i = 0; i < text.length / 2; i++ ) {
            if ( text.length % 2 != 0 ) {
                random += text[ i ];
            }
        }

        return random;

    }

    private static byte[] encryption( int key, String msg ) {

        //TODO: implement step c (encrypt the message)

        byte[] encrypted =new byte[]{};
        String strKey = Integer.toString( key );

        try {
            SecretKeySpec skeyspec = new SecretKeySpec( strKey.getBytes( ), "Blowfish" );
            Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
            cipher.init( Cipher.ENCRYPT_MODE, skeyspec );
            encrypted = cipher.doFinal( msg.getBytes( ) );

        }
        catch ( Exception e ) {
            e.printStackTrace( );

        }
        return encrypted;
    }

    private static void bruteForce( byte[] msgE ) {

        //TODO: implement bruteForce algorithm, you may need the above decryption(key,msgE) method


        decryption( key, msgE );
    }

    private static void decryption( int key, byte[] msgE ) {

        //TODO: implement step d (decryption)

        String strKey = Integer.toString( key );
        String strData = "";

        try {
            SecretKeySpec skeyspec = new SecretKeySpec( strKey.getBytes( ), "Blowfish" );
            Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
            cipher.init( Cipher.DECRYPT_MODE, skeyspec );
            byte[] decrypted = cipher.doFinal( msgE);
            strData = new String( decrypted );

        }
        catch ( Exception e ) {
            e.printStackTrace( );

        }
        System.out.println( strData );
    }
}

示例 2) 使用 CBC 模式的代码

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.util.Random;

public class Assignment1Demo {

    private static String msg;

    private static byte[] msgE;

    private static String msgD;

    private static int key;
    private static byte[] encodedParams;

    public static void main( String[] args ) {

        //TODO: You can only call methods in main method

        key = generateKey( );

        msg = generateMsg( );

        msgE = encryption( key, msg );

        bruteForce( msgE );

    }

    private static int generateKey( ) {

        //TODO: implement step a (randomly generate 16-bit key)

        //16 bit digit means 2^16 -1 in decimal

        Random rand = new Random( );

        return rand.nextInt( ( int ) ( Math.pow( 2, 16 ) - 1 ) );

    }

    private static String generateMsg( ) {

        //TODO: implement step b (randonly generate a string with an even number of characters)
        String chractersU = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String chractersL = chractersU.toLowerCase( );
        String space = " ";
        String alphanum = chractersU + space + chractersL;
        String random = "";
        int length = alphanum.length( );
        Random rand = new Random( );
        char[] text = new char[ length ];

        for ( int i = 0; i < length; i++ ) {
            text[ i ] = alphanum.charAt( rand.nextInt( alphanum.length( ) ) );
        }

        for ( int i = 0; i < text.length / 2; i++ ) {
            if ( text.length % 2 != 0 ) {
                random += text[ i ];
            }
        }

        return random;

    }

    private static byte[] encryption( int key, String msg ) {

        //TODO: implement step c (encrypt the message)

        byte[] encrypted =new byte[]{};
        String strKey = Integer.toString( key );

        try {
            SecretKeySpec skeyspec = new SecretKeySpec( strKey.getBytes( ), "Blowfish" );
            Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
            cipher.init( Cipher.ENCRYPT_MODE, skeyspec );
            encrypted = cipher.doFinal( msg.getBytes( ) );
            encodedParams = cipher.getParameters().getEncoded();
        }
        catch ( Exception e ) {
            e.printStackTrace( );

        }
        return encrypted;
    }

    private static void bruteForce( byte[] msgE ) {

        //TODO: implement bruteForce algorithm, you may need the above decryption(key,msgE) method


        decryption( key, msgE );
    }

    private static void decryption( int key, byte[] msgE ) {

        //TODO: implement step d (decryption)

        String strKey = Integer.toString( key );
        String strData = "";

        try {
            SecretKeySpec skeyspec = new SecretKeySpec( strKey.getBytes( ), "Blowfish" );
            Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
            AlgorithmParameters params = AlgorithmParameters.getInstance("Blowfish");
            params.init(encodedParams);
            cipher.init( Cipher.DECRYPT_MODE, skeyspec, params );
            byte[] decrypted = cipher.doFinal( msgE);
            strData = new String( decrypted );

        }
        catch ( Exception e ) {
            e.printStackTrace( );

        }
        System.out.println( strData );
    }
}

简而言之,您正在使用加密的二进制数据并假设它是有效文本,但一般来说它不会。

您可以使用 UTF-8 编码将字符串编码为 byte[],但并非所有可能的 byte[] 编码都是有效的字符串。加密数据时,可以使用任何可能的字节,但不能总是使用标准字符编码将其转换为字符串。这可能会导致数据长度发生变化,这就是为什么它不再像数据编码时那样是 8 的倍数。

将数据编码为可打印字符串的一种简单方法是使用 Base64 编码。

encryption中使用

        strData = DatatypeConverter.printBase64Binary(encrypted);

并在 decryption 中使用

        byte[] decrypted=cipher.doFinal(DatatypeConverter.parseBase64Binary(msgE));

这样您将始终尝试解密您编写的二进制数据。

另一种选择是不尝试将二进制数据存储在字符串中,而只使用原始 byte[]


EDIT 你能试试这个吗,使用 byte[] 而不是 String

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class A {
    public static void main(String... args) {

        String text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        for (int i = 0; i < text.length(); i++) {
            byte[] encrypt = encryption(1, text.substring(0, i));
            String hello = decryption(1, encrypt);
            System.out.println(hello);
        }
    }

    private static byte[] encryption(int key, String msg) {
        String strKey = Integer.toString(key);
        try {
            SecretKeySpec skeyspec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
            Cipher cipher = Cipher.getInstance("Blowfish");
            cipher.init(Cipher.ENCRYPT_MODE, skeyspec);
            return cipher.doFinal(msg.getBytes(StandardCharsets.UTF_8));

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    private static String decryption(int key, byte[] msgE) {
        String strKey = Integer.toString(key);
        try {
            SecretKeySpec skeyspec = new SecretKeySpec(strKey.getBytes(), "Blowfish");
            Cipher cipher = Cipher.getInstance("Blowfish");
            cipher.init(Cipher.DECRYPT_MODE, skeyspec);
            byte[] decrypted = cipher.doFinal(msgE);
            return new String(decrypted, StandardCharsets.UTF_8);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}