1

I am trying to decrypt the encrypted string from Rijndael VBA code. Java 8 code

public static void Decrypt() throws Exception{
        String mydata = "3m/WeZ1cAUEqexeH64gPehkMdQSRvx7K9TKhtpUfEg==";

        byte[] encryptedBytes = Base64.getDecoder().decode(mydata);
        byte[] key = Base64.getDecoder().decode("VGhpcnR5VHdvQnl0ZXMzJFRoaXJ0eVR3b0J5dGVzMyQ=");          
        byte[] iv = Base64.getDecoder().decode("MyRUaHJlZVR3b0J5dGVzMzMkVGhyZWVUd29CeXRlczM=");
        
        PaddedBufferedBlockCipher bufferedBlock = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RijndaelEngine(256)), new PKCS7Padding());
        CipherParameters keyAndIV = new ParametersWithIV(new KeyParameter(key), iv);
        bufferedBlock.init(false, keyAndIV);        
        
        byte[] decryptedBytes = new byte[bufferedBlock.getOutputSize(encryptedBytes.length)];
        int processed = bufferedBlock.processBytes(encryptedBytes, 0, encryptedBytes.length, decryptedBytes, 0);
        processed += bufferedBlock.doFinal(decryptedBytes, processed);
                
        System.out.println(new String(decryptedBytes, 0, processed, StandardCharsets.UTF_8));       
    }

Above code is giving me an error "last block incomplete in decryption" in line

processed += bufferedBlock.doFinal(decryptedBytes, processed);

This is the VBA Code encryption:

Function Encrypt(plaintext, aesKey)
    Dim cipherBytes, aesKeyBytes, ivKeyBytes, plainBytes() As Byte
    
    Dim utf8, AES, aesEnc, cipherMode As Object
    Dim aesIV() As Byte
        
    Set AES = CreateObject("System.Security.Cryptography.RijndaelManaged")
    Set utf8 = CreateObject("System.Text.UTF8Encoding")
   
    AES.KeySize = 256
    AES.BlockSize = 256
    'CipherMode.CBC
    AES.Mode = 1
    'PaddingMode.PKCS7
    AES.Padding = 2
    AES.Key = utf8.GetBytes_4("ThirtyTwoBytes3$ThirtyTwoBytes3$")
    AES.IV = utf8.GetBytes_4("3$ThreeTwoBytes33$ThreeTwoBytes3")
    plainBytes = utf8.GetBytes_4(plaintext)
    'plainBytes = B64Decode(plaintext)
    'Set aesEnc = AES.CreateEncryptor_2((aesKeyBytes), (ivKeyBytes))
    cipherBytes = AES.CreateEncryptor().TransformFinalBlock((plainBytes), 0, UBound(plainBytes))
    Encrypt = B64Encode(cipherBytes)
End Function

I am trying to encrypt the data sent by VBA and decrypt and use it. Help me in correcting the Java code to match the VBA code VBA AES CBC encryption

Pat
  • 535
  • 1
  • 16
  • 41
  • 2
    Your key and iv are not Base64-encoded so any try to 'decode' them has to fail. Second: you should change the line 'byte[] encryptedBytes = Base64.getDecoder().decode(mydata.getBytes(StandardCharsets.UTF_8));' to 'byte[] encryptedBytes = Base64.getDecoder().decode(mydata);' – Michael Fehr Sep 10 '20 at 22:23
  • 1
    In the VBA code you seem to use CBC ([`AES.Mode = 1`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.ciphermode?view=netcore-3.1#fields)) as mode and PKCS7 ([`AES.Padding = 2`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.paddingmode?view=netcore-3.1#fields)) as padding (both are also the default for `RijndaelManaged`). In the Java code CBC and Zero Padding are applied, i.e. there is an inconsistency regarding the padding. – Topaco Sep 11 '20 at 07:08
  • You also use a block size of 256 bits. Since this is done consistently in both codes, this works. However, it is then no longer AES. [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) is a subset of Rijndael that applies only one block size, namely 128 bits. – Topaco Sep 11 '20 at 07:10
  • @MichaelFehr Thanks, updated the code and now i am getting "last block incomplete in decryption" – Pat Sep 11 '20 at 14:56
  • @Topaco, Thanks, I updated code to PKCS7 padding and in above question. I have other runtime error, but did my java code is consistent with VBA now? – Pat Sep 11 '20 at 14:58
  • The ciphertext `3m/WeZ...` is Base64 decoded only 31 bytes long, it should be an integer multiple of the block size (here 32 bytes). Was the ciphertext created with the VBA code? – Topaco Sep 11 '20 at 15:58
  • @Topaco Yes correct that ciphertext is from VBA code – Pat Sep 11 '20 at 16:15
  • Then there is probably an issue in your VBA code. It's easier to find the problem if you know the ciphertext to your plaintext, key and IV. Therefore it's useful to switch to a block size of 128 bits (AES), because this is the default and you can determine the ciphertext easier with 3rd party tools, e.g. [here](https://cryptii.com/pipes/aes-encryption) or [here](https://gchq.github.io/CyberChef/). Note that with AES you can only use a 16 bytes IV. – Topaco Sep 11 '20 at 18:07
  • @Topaco, may be the problem is with the AES.Key = utf8.GetBytes_4("ThirtyTwoBytes3$ThirtyTwoBytes3$") ? They say "Thirty..." is plain text and they convert it to bytes[] – Pat Sep 11 '20 at 18:57
  • No, it's not the key. I've identified the problem, please see my answer. – Topaco Sep 11 '20 at 21:19

1 Answers1

2

The problem is that in both B64Encode() and encrypt() the length of the byte array is specified incorrectly with UBound() (see Fix 1, Fix 2 in the code). UBound() returns the largest index, so the length is UBound() + 1. Alternatively LenB() can be used.

The following VBA code encrypts a plaintext:

Function Min(a, b)
    Min = a
    If b < a Then Min = b
End Function

Function B64Encode(bytes)
    Set b64Enc = CreateObject("System.Security.Cryptography.ToBase64Transform")
    Set utf8 = CreateObject("System.Text.UTF8Encoding")
    BlockSize = b64Enc.InputBlockSize
    For Offset = 0 To LenB(bytes) - 1 Step BlockSize                        ' LenB(bytes) - 1 --> UBound(bytes)
        Length = Min(BlockSize, LenB(bytes) - Offset)                       ' LenB(bytes)     --> UBound(bytes) + 1                             Fix 1
        b64Block = b64Enc.TransformFinalBlock((bytes), Offset, Length)
        result = result & utf8.GetString((b64Block))
    Next
    B64Encode = result
End Function

Function encrypt(plaintext)
    Set AES = CreateObject("System.Security.Cryptography.RijndaelManaged")
    Set utf8 = CreateObject("System.Text.UTF8Encoding")
    AES.KeySize = 256
    AES.BlockSize = 256
    AES.Mode = 1 'CipherMode.CBC
    AES.Padding = 2 'PaddingMode.PKCS7
    AES.Key = utf8.GetBytes_4("ThirtyTwoBytes3$ThirtyTwoBytes3$")
    AES.IV = utf8.GetBytes_4("3$ThreeTwoBytes33$ThreeTwoBytes3")
    plainBytes = utf8.GetBytes_4(plaintext)
    cipherBytes = AES.CreateEncryptor().TransformFinalBlock((plainBytes), 0, LenB(plainBytes)) ' LenB(plainBytes) --> UBound(plainBytes) + 1    Fix 2
    encrypt = B64Encode(cipherBytes)
End Function

Sub encryptData()
Debug.Print encrypt("The quick brown fox jumps over the lazy dog") ' 2WVYo0DvgbKXBUn+/eI/yTvUJs0zYxEN9lU5ytxhJRWPDnRn5y4HuwPjaMSg47gTG4dc2ABL5EyIvDg1N91T5A==
End Sub

which can be decrypted with the following Java code (using BouncyCastle):

public static void Decrypt() throws Exception{
    String mydata = "2WVYo0DvgbKXBUn+/eI/yTvUJs0zYxEN9lU5ytxhJRWPDnRn5y4HuwPjaMSg47gTG4dc2ABL5EyIvDg1N91T5A==";

    byte[] encryptedBytes = Base64.getDecoder().decode(mydata);
    byte[] key = "ThirtyTwoBytes3$ThirtyTwoBytes3$".getBytes(StandardCharsets.UTF_8);          
    byte[] iv = "3$ThreeTwoBytes33$ThreeTwoBytes3".getBytes(StandardCharsets.UTF_8);       
    //byte[] key = Base64.getDecoder().decode("VGhpcnR5VHdvQnl0ZXMzJFRoaXJ0eVR3b0J5dGVzMyQ=");  // works also with Base64 encoded key and IV        
    //byte[] iv = Base64.getDecoder().decode("MyRUaHJlZVR3b0J5dGVzMzMkVGhyZWVUd29CeXRlczM=");
    
    PaddedBufferedBlockCipher bufferedBlock = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RijndaelEngine(256)), new PKCS7Padding());
    CipherParameters keyAndIV = new ParametersWithIV(new KeyParameter(key), iv);
    bufferedBlock.init(false, keyAndIV);        
    
    byte[] decryptedBytes = new byte[bufferedBlock.getOutputSize(encryptedBytes.length)];
    int processed = bufferedBlock.processBytes(encryptedBytes, 0, encryptedBytes.length, decryptedBytes, 0);
    processed += bufferedBlock.doFinal(decryptedBytes, processed);
            
    System.out.println(new String(decryptedBytes, 0, processed, StandardCharsets.UTF_8));       
}

Please note that the code uses Rijndael with a block size of 256 bytes and therefore no AES. AES is a subset of Rijndael with a block size of 128 bytes. It makes more sense to apply AES, as this is the standard (Rijndael is not).

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thank you so much, works perfect. Thanks again for taking time to respond. Do you have any Sample code or reference for 128 bytes AES, for both VBA and Java? Thanks in advance – Pat Sep 11 '20 at 22:04
  • 1
    @Pat - The codes are essentially identical. For AES, you only have to set `AES.BlockSize = 128` in the VBA code. In the Java code `new RijndaelEngine(128)` must be used. Furthermore, for AES only a 16 bytes IV must be applied, because the size of the IV always corresponds to the block size. – Topaco Sep 11 '20 at 22:22
  • Thanks again, will try that too. You are awesome, you made my day, it was long running issue and it is resolved now. – Pat Sep 11 '20 at 22:24