Implementing solution to IllegalBlockSizeException in Java?

891 views Asked by At

I'm trying to fix my code. In my code, I'm trying to generate a 16bit key which I've already done. Secondly, generate a random message which is also done. Encrypt and decrypt the data which I'm getting errors on. Lastly have a brute force algorithm to decrypt the message which i'll try doing later. So for my encryption the code encrypts it but doesn't encrypt the random generated string. I'm getting bunch of errors.

My Code:

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);
}}

Errors:

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)
2

There are 2 answers

15
Victor On BEST ANSWER

Your string is randomly generated, but when the block size does not match what the cipher expects, you need to apply an acceptable padding algorithm, so the size of your input is aligned with algorithm. This is applicable to all block ciphers.

This example is a simple meter for a cipher, not that one could use something like as algorithm "AES/CBC/PKCS5Padding" "RC2/CBC/PKCS5Padding" "DESede/CBC/PKCS5Padding (PKCS#5 padding is defined for 8-byte block sizes)

@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;
    }

Another example without the streams:

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

It is really not necessary, but if you wanna go with the base64, you can look for Apache: http://commons.apache.org/proper/commons-codec/


Examples using your code

1) ECB mode

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 );
    }
}

Example 2) your code with CBC mode

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 );
    }
}
19
Peter Lawrey On

In short, you are using encrypted binary data and assuming it is valid text, which generally speaking it won't be.

You can encode a String as a byte[] using UTF-8 encoding, but not all possible byte[] encoding are valid Strings. When you encrypt data, any possible byte could be used, but this cannot always be turned into a String using standard character encoding. This can result in the length of data changing which is why it is no longer a multiple of 8 as it was when the data was encoded.

A simple way to encode data as a printable string is to use Base64 encoding.

In encryption use

        strData = DatatypeConverter.printBase64Binary(encrypted);

and in decryption use

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

This way you will always be attempting to decrypt the binary data you wrote.

Another option is to not try to store binary data in a String, and just use the original byte[]


EDIT can you try this, using byte[] instead of a 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);
        }
    }
}