Search code examples
javaencryptionbouncycastle

I block-padded and still get "data not block size aligned"


I have a problem when encrypting that causes the error:

javax.crypto.IllegalBlockSizeException: data not block size aligned
    at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
    at javax.crypto.Cipher.doFinal(Cipher.java:2086)
    at com.lcp.sso.logic.SsoCipher.encode(SsoCipher.java:89)

The constructor for the object:

public MyCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException {
    Security.addProvider(new BouncyCastleProvider());
    KeyGenerator keyGen = KeyGenerator.getInstance("DESede", "BC");
    keyGen.init(new SecureRandom());
    SecretKey keySpec = keyGen.generateKey();

    this.sharedKey = keySpec.getEncoded().toString();
    this.encrypter = Cipher.getInstance("DESede/ECB/Nopadding", "BC");
    this.encrypter.init(Cipher.ENCRYPT_MODE, keySpec);
    this.decrypter = Cipher.getInstance("DESede/ECB/Nopadding", "BC");
    this.decrypter.init(Cipher.DECRYPT_MODE, keySpec);
}

The method in which the error occurs:

public String encode(String arg_text) throws IllegalBlockSizeException, BadPaddingException {
    byte[] encrypt = arg_text.getBytes();

    if(encrypt.length % 8 != 0){ //not a multiple of 8
        //create a new array with a size which is a multiple of 8
        byte[] padded = new byte[encrypt.length + 8 - (encrypt.length % 8)];

        //copy the old array into it
        System.arraycopy(encrypt, 0, padded, 0, encrypt.length);
        encrypt = padded;
    }

    byte[] b = Base64.encodeBase64URLSafe(encrypt);
    return Base64.encodeBase64String(encrypter.doFinal(b));
}

The error happens when calling that last method there. I'd swear that I'm making it the right block size, there, by null-padding the byte array to make sure it's a multiple of 8. I just don't know what went wrong!

I'm using:
Eclipse Version: Juno Service Release 1
Server: Tomcat v7.0 Server at Localhost ( specifically 7.0.32 )

--- EDIT ---

Program isn't working yet, (EDIT: YES IT IS! Muahahahaha!) but this problem is solved.

The constructor for the object:

public MyCipher() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, InvalidParameterSpecException, InvalidAlgorithmParameterException {
    Security.addProvider(new BouncyCastleProvider());
    KeyGenerator keyGen = KeyGenerator.getInstance("DES", "BC");
    keyGen.init(new SecureRandom());
    SecretKey keySpec = keyGen.generateKey();

    this.sharedKey = new String( Base64.encodeBase64URLSafe( keySpec.getEncoded() ) );
    this.encrypter = Cipher.getInstance("DES/CBC/PKCS5Padding", "BC");
    this.encrypter.init(Cipher.ENCRYPT_MODE, keySpec);

    AlgorithmParameters params = this.encrypter.getParameters();
    byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
    IvParameterSpec ivSpec = new IvParameterSpec(iv);

    this.sharedIV = new String( Base64.encodeBase64URLSafe( iv ) );
    this.decrypter = Cipher.getInstance("DES/CBC/PKCS5Padding", "BC");
    this.decrypter.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
}

And the encrypting method is:

public String encode(String arg_text) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
    byte[] encrypt = arg_text.getBytes();
    return new String( Base64.encodeBase64URLSafe(encrypter.doFinal(encrypt)), "US-ASCII");
}

It's now working to encrypt and decrypt just fine. Thank you VERY much.


Solution

  • Strange code in some aspects. Begining by why your code doesn't work, you are making sure that the size of encrypt is a multiple of 8, but you are trying to cipher byte[] b = Base64.encodeBase64URLSafe(encrypt); which may not be a multiple of 8. The following code should work:

    public String encode(String arg_text) throws IllegalBlockSizeException, BadPaddingException {
        byte[] encrypt = arg_text.getBytes();
    
        if(encrypt.length % 8 != 0){ //not a multiple of 8
            //create a new array with a size which is a multiple of 8
            byte[] padded = new byte[encrypt.length + 8 - (encrypt.length % 8)];
    
            //copy the old array into it
            System.arraycopy(encrypt, 0, padded, 0, encrypt.length);
            encrypt = padded;
        }
    
        return new String(Base64.encodeBase64URLSafe(encrypter.doFinal(b)), "US-ASCII");
    }
    

    Now, where there is this.encrypter = Cipher.getInstance("DESede/ECB/Nopadding", "BC"); do you notice the "Nopadding" part of the string? Well, the code does what you ask it to do... but the library can do the padding work for you, you just have to tell it. Try this.encrypter = Cipher.getInstance("DESede/ECB/PKCS5Padding", "BC"); instead and see if it works.

    But really, why would you want to use 3DES in ECB mode? Unless the reason is legacy (which is unlikely from what I see here), it makes no sense. I believe you need to read a bit more about cryptography.