B4J Question AES128 ECB Encrypt

aaronk

Well-Known Member
Licensed User
Longtime User
Hi,

I am trying to encrypt a message using AES128 ECB.

I ended up working out how to decrypt the message, but can't work out how to encrypt the message back again.

I have tried used the following code..
(not sure if this is correct or not)

B4X:
Sub Encrypt As String
    
    Dim Key As String = "0123456789ABCDEF" ' MDEyMzQ1Njc4OUFCQ0RFRg==
    Dim bc As ByteConverter
    Dim kg As KeyGenerator
    
    Dim C As Cipher
    C.Initialize("AES/ECB/PKCS5Padding")
    
    kg.Initialize("AES")
    kg.KeyFromBytes(Key.GetBytes("UTF8"))
      
    Dim textToEncrypt As String = "0000754C//00409D27558D//05.03.10//02.00.44//Hello"
    Dim textBytes() As Byte = textToEncrypt.GetBytes("ASCII")
    Log($"textBytes.Length = ${textBytes.Length}"$)
    Log(bc.HexFromBytes(textBytes))
    Dim textEncryptedBytes() As Byte = C.Encrypt(textBytes, kg.Key, False)
    Log($"textEncryptedBytes.Length = ${textEncryptedBytes.Length}"$)
    Log(bc.HexFromBytes(textEncryptedBytes))
 
    Dim textLength As Int  = textBytes.Length
    Log($"textLength = ${textLength}"$)
    Dim byteBlocks As Int = textLength / 16
    If (textLength Mod 16) > 0 Then byteBlocks = byteBlocks + 1
    Log($"byteBlocks * 16 = ${byteBlocks *16}"$)
    Dim textPadded(byteBlocks * 16) As Byte
    bc.ArrayCopy(textBytes, 0, textPadded, 0, textBytes.Length)
    Log($"textPadded.Length = ${textPadded.Length}"$)
    Log(bc.HexFromBytes(textPadded))
    For x = 0 To byteBlocks - 1
        Dim buffer(16) As Byte
        bc.ArrayCopy(textPadded, x * 16, buffer, 0, 16)
        Dim bufferEncrypted() As Byte = C.Encrypt(buffer, kg.Key, False)
        Log($"bufferEncrypted: ${bc.StringFromBytes(bufferEncrypted,"UTF-8")}"$)
    Next

End Sub

It seems to log:

textBytes.Length = 49
30303030373534432F2F3030343039443237353538442F2F30352E30332E31302F2F30322E30302E34342F2F48656C6C6F
textEncryptedBytes.Length = 64
CB517EF65D218017C806EE2D4CF03E1A7859FD08E48E1F1D799E9A8D8593041ED6BC07D879092756A99BE059CA98BFEBAB2E5093707E61AC225B3B5C1B3F8D06
textLength = 49
byteBlocks * 16 = 64
textPadded.Length = 64
30303030373534432F2F3030343039443237353538442F2F30352E30332E31302F2F30322E30302E34342F2F48656C6C6F000000000000000000000000000000
bufferEncrypted: �Q~�]!���-L�>4�3l�5Vs�c���
bufferEncrypted: xY��y�����4�3l�5Vs�c���
bufferEncrypted: ּ�y 'V���Yʘ��4�3l�5Vs�c���
bufferEncrypted: E׿%R=���0�t���4�3l�5Vs�c���

However the final message should be:
�q�!DAZ��R��:f/�Ŝ��h�U�0h�V��ƅHw��s�Č)�o�yZ��%v��_,"

Any ideas on what I have done wrong ?
 

OliverA

Expert
Licensed User
Longtime User
A)
You are not using the same key/same input/or combo of both. If you go to this site (https://www.devglan.com/online-tools/aes-encryption-decryption) and enter

0000754C//00409D27558D//05.03.10//02.00.44//Hello

for your text to be encrypted and select ECB as the mode with 128 bit key size and enter

0123456789ABCDEF

as your key and pick HEX output, it will produce the same output as above

CB517EF65D218017C806EE2D4CF03E1A7859FD08E48E1F1D799E9A8D8593041ED6BC07D879092756A99BE059CA98BFEBAB2E5093707E61AC225B3B5C1B3F8D06

Therefore 1) Your key is not correct, or 2) your input string is not correct or 3) both key and input are incorrect

B)
This does not affect you yet, but you have the incorrect padding. In your previous post you posted the VB code that is used for encryption (https://www.b4x.com/android/forum/threads/aes128-ecb-decrypt.108828/#post-680483). That code uses zero padding. Therefore you need to use zero padding in your encryption formula. Zero padding is not an option with what Java includes, but by including Bouncy Castle, you get the zero padding option. The code I posted in the other thread shows you how to include Bouncy Castle as an encryption provider that allows you to use zero padding without managing your own padding (which you should not. Encryption is already hard enough without complicating things).
 
Upvote 0

aaronk

Well-Known Member
Licensed User
Longtime User
as your key and pick HEX output, it will produce the same output as above
Isn't that correct? (maybe I am getting confused with all this encryption thing)

This part of the code (from my first post):
B4X:
bc.HexFromBytes(textEncryptedBytes)
is returning:
CB517EF65D218017C806EE2D4CF03E1A7859FD08E48E1F1D799E9A8D8593041ED6BC07D879092756A99BE059CA98BFEBAB2E5093707E61AC225B3B5C1B3F8D06

This is the same HEX as shown on https://www.devglan.com/online-tools/aes-encryption-decryption

Encrypt:
0000754C//00409D27558D//05.03.10//02.00.44//Hello
AES 128
Secret key 0123456789ABCDEF
Output as HEX
This is returning the following.
CB517EF65D218017C806EE2D4CF03E1A7859FD08E48E1F1D799E9A8D8593041ED6BC07D879092756A99BE059CA98BFEBAB2E5093707E61AC225B3B5C1B3F8D06

Isn't that what I need ?

Here is how I did the decrypting:
B4X:
Sub Decrypt(value() As Byte) As String

    Dim raw() As Byte = value
    bb.Initialize
    bb.Append(raw)
    Dim binaryflag() As Byte = Array As Byte(0x33, 0x36, 0x37, 0x30, 0x42, 0x42, 0x39, 0x46, 0x2f,0x2f,0x31,0x2f,0x2f)
    If bb.IndexOf(binaryflag) > -1 Then
        Dim msgbytes() As Byte = bb.SubArray2(13,bb.IndexOf2(Array As Byte(0x0),13)) ' use the index of the zerobyte to mark the "end".
    End If

    kg.Initialize("AES")
    kg.KeyFromBytes(Key.GetBytes("UTF8"))
    
    C.Initialize("AES/ECB/NoPadding")

    msgbytes = C.Decrypt(msgbytes, kg.Key, False)

    Return BConv.StringFromBytes(msgbytes, "UTF8").Trim
    
End Sub

Above works fine.

I tried:
B4X:
Sub Encrypt(textToEncrypt As String) As String
    
    Dim Key As String = "0123456789ABCDEF"
    Dim bc As ByteConverter
    Dim kg As KeyGenerator
    
    Dim C As Cipher
    C.Initialize("AES/ECB/PKCS5Padding")
    
    kg.Initialize("AES")
    kg.KeyFromBytes(Key.GetBytes("UTF8"))
    
    Dim textBytes() As Byte = textToEncrypt.GetBytes("ASCII")
    Log($"textBytes.Length = ${textBytes.Length}"$)
    Log(bc.HexFromBytes(textBytes))   
    
    Dim textEncryptedBytes() As Byte = C.Encrypt(textBytes, kg.Key, False)
    Log($"textEncryptedBytes.Length = ${textEncryptedBytes.Length}"$)
    Log(bc.HexFromBytes(textEncryptedBytes))
    
    Return bc.StringFromBytes(textEncryptedBytes,"ASCII")
    
End Sub

This encrypts the message, and returns the value, but I think it's wrong for some reason.

Original Encrypted message is:
3670BB9F//1//�Q~�]!���-L�>xY��y�����ּ�y 'V���Yʘ���d��q{�ۺ�Y��

When I decrypt it:
B4X:
Log("Decrypt = " & Decrypt(Packet.Data))
it logs:
Decrypt = 0000754C//00409D27558D//05.03.10//02.00.44//Hello

Which is correct.

If I encrypt the message:
B4X:
Log("Encrypt = " & Encrypt("0000754C//00409D27558D//05.03.10//02.00.44//Hello"))

It returns:
Encrypt = �Q~�]!���-L�>xY���y��������y 'V���Y�����.P�p~a�"[;\?�

It looks like I have it done wrong, but can't work out the encryption function. Been trying all day :(

The AES key is 0123456789ABCDEF during all my testing.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Read point B again. You are using the wrong padding for the encryption.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
You are still making the same mistake.

This line is completely wrong:
B4X:
 Return bc.StringFromBytes(textEncryptedBytes,"ASCII")

As I've previous wrote (https://www.b4x.com/android/forum/threads/aes128-ecb-decrypt.108828/#post-680140):

Never convert "random" bytes to string.
Only bytes that represent a string can be converted to a string.

Worth repeating: never convert random bytes to string. It will not work.

Use base64 encoding to convert the bytes to string.

Encrypted data doesn't represent a string.
 
Upvote 0

aaronk

Well-Known Member
Licensed User
Longtime User
Use base64 encoding to convert the bytes to string.
I did try base64 encoding, but couldn't get it to work as well.

Are you able to help with the encryption function?

I am not 100% on how to do this, and getting totally confused with this.
 
Upvote 0

aaronk

Well-Known Member
Licensed User
Longtime User
You seem to be ignoring my suggestions.
I am not ignoring the suggestions, just don't understand how to do it as everything I try doesn't work.

Do you mean:

B4X:
Sub Encrypt
    Dim bc As ByteConverter
    
    Dim textToEncrypt As String = "0000754C//00409D27558D//05.03.10//02.00.44//Hello"
    
    Log("textToEncrypt hex = " & bc.HexFromBytes(textToEncrypt.GetBytes("UTF8")))
    
    Dim Key As String = "0123456789ABCDEF"
    
    Dim kg As KeyGenerator
    
    Dim C As Cipher
    C.Initialize("AES/ECB/PKCS5Padding")
    
    kg.Initialize("AES")
    kg.KeyFromBytes(Key.GetBytes("UTF8"))
    
    Dim textBytes() As Byte = textToEncrypt.GetBytes("ASCII")
    Log($"textBytes.Length = ${textBytes.Length}"$)
    Log(bc.HexFromBytes(textBytes))
    
    Dim textEncryptedBytes() As Byte = C.Encrypt(textBytes, kg.Key, False)
    Log($"textEncryptedBytes.Length = ${textEncryptedBytes.Length}"$)
    Log("HexFromBytes = " & bc.HexFromBytes(textEncryptedBytes))
    
    Dim su As StringUtils
    Log(su.EncodeBase64(textEncryptedBytes))
        
End Sub
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
If you are trying to replicate the code that he have shown here https://www.b4x.com/android/forum/threads/aes128-ecb-decrypt.108828/#post-680483, then you need to work on the raw data bytes of the incoming/outgoing packet. Looking at that code (the VB/whatever code), the encryption steps are as follows:

0) Create a buffer to hold encrypted data (of a fixed size IOBuffSize)
1) Figure out how many pad bytes (zeroes) are needed to 16 byte align the byte array data
2) Re-dim the incoming byte array to the new size (size of original array + number of pad bytes)
3) Manually pad new re-dimmed incoming byte array
4) In a loop, start encrypting the incoming byte array from the 14th position, 16 bytes at a time and store results in encrypted data buffer
5) Write encrypted data buffer to incoming data buffer from position 14 and on

So first, how does VB convert a string to bytes? What encoding does it use? The same encoding has to be used on the B4J side (for both the packet data and the key bytes used to encrypt the data). One of the things I notice with the VB code is that the key is pass ByVal. So if any changes are made to the key, the encryption changes, the output changes. So make sure that the key is really the key you think you use on the VB side or the encoding on the VB side will not match the encoding on the B4J side.

So a method that should produce the same results as your VB function could be:

B4X:
Sub Encrypt(data() As Byte, key() As Byte) As Byte()
   Dim kg As KeyGenerator
   Dim C As Cipher
   Dim bc As ByteConverter
   Dim bb As B4XBytesBuilder ' Part of B4XCollections library
   bb.Initialize
   
   kg.Initialize("AES")
   kg.KeyFromBytes(key)
   
   C.Initialize("AES/ECB/ZeroBytePadding") ' We let crypto library take care of padding. We're lazy

   'Extract first 13 bytes from data()   
   Dim firstThirteenBytes(13) As Byte
   bc.ArrayCopy(data, 0, firstThirteenBytes, 0, 13)
   
   'Extract data to be encrypted from data()
   Dim toEncryptBytes(data.Length - 13) As Byte
   bc.ArrayCopy(data, 13, toEncryptBytes, 0, data.Length - 13)
   
   'Encrypt
   Dim encryptedBytes() As Byte = C.Encrypt(toEncryptBytes, kg.Key, False)

   'Recreate the data packet from first 13 bytes from data() and encrypted information
   bb.Append(firstThirteenBytes)
   bb.Append(encryptedBytes)
   Return(bb.ToArray)
End Sub

So the steps now are just:
1) Split packet into two parts
2) Encrypt second part
3) Create packet from first part and newly encrypted data

Usage:
B4X:
'Non-UI application (console / server application)
#Region Project Attributes
   #CommandLineArgs:
   #MergeLibraries: True
#End Region

#AdditionalJar: bcprov-jdk15on-160.jar
'       External Libraries:
'           #AdditionalJar: bcprov-jdk15on-160
'           Library can be found here: https://www.bouncycastle.org/download/bcprov-jdk15on-160.jar

Sub Process_Globals
   
End Sub

Sub AppStart (Args() As String)
   Dim joSecurity As JavaObject
   joSecurity.InitializeStatic("java.security.Security")
#if B4J
   Dim jo As JavaObject
   jo.InitializeNewInstance("org.bouncycastle.jce.provider.BouncyCastleProvider", Null)
   joSecurity.RunMethod("addProvider", Array As Object (jo))
#else if B4A
   ' Seems to just work as of Android 5.1 (oldest version I have)
'   Dim jo As JavaObject
'   jo.InitializeNewInstance("org.spongycastle.jce.provider.BouncyCastleProvider", Null)
'   joSecurity.RunMethod("insertProviderAt", Array As Object (jo, 1))
#else
   LOG("ERROR: UNSUPPORTED PLATFORM")
   Return
#End If

   Dim bc As ByteConverter
   
   Dim stringData As String = "3670BB9F//1//0000754C//00409D27558D//05.03.10//02.00.44//Hello"
   Dim stringPassword As String = "0123456789ABCDEF"
   
   Dim bytesData() As Byte = stringData.GetBytes("ASCII") ' This needs to match the encoding used on the VB side
   Dim bytesPassword() As Byte = stringPassword.GetBytes("ASCII") ' This needs to match the encoding used on the VB Side
   
   Log($"bytesData: ${bc.HexFromBytes(bytesData)}"$)           'Should match VB's byte data if dumped to HEX
   Log($"bytesPassword: ${bc.HexFromBytes(bytesPassword)}"$)   'Should match VB's byte key if dumped to HEX
   
   Dim encryptedPacket() As Byte = Encrypt(bytesData, bytesPassword)
   'encryptedPacket() now contains the data packet that needs to be sent via UDP
   
   Log($"encryptedPacket: ${bc.HexFromBytes(encryptedPacket)}"$)   'Should match VB's encrypted packet if dumped to HEX
   
End Sub
 
Upvote 0
Top