Android Tutorial [B4x] AES Encryption (lessons learned & best practice)

Last weekend I migrated my XAMPP test environment (Apache/PHP/MySql). It came with PHP 7.x. Yesterday I wanted to test one of my Apps using AES and Mcrypt for it in PHP but it didn't work no more. I noticed that MCrypt was deprecated anyway and even was removed due to security and other issues from PHP 7.x. So I starded searching for an alternative for PHP which is matching B4x (I use agraham's Encryption lib: https://www.b4x.com/android/forum/threads/base64-and-encryption-library.6839/#content)

Soon I came across OpenSSL which I use for RSA encryption on the PHP side for some years and tried to migrate my code to it. Until then I was using AES-128 Bit. After reading some articles I learned some new things which I want to write down here. Using AES is quite simple if you know the details. Not knowing these details makes your encryption usafe.

Even on my production servers which use RSA (https://) I use AES addtionally and encrypt ALL data I send/receive to/from them.

However: AES uses

- a key (the data is encrypted with this key). The key is either 128, 192 or 256 Bits long (aka AES-128, AES-192 and AES-256). As a string it has a length of 16,24 or 32 chars/bytes. So noch chance to use other lengths

AES is a variant of Rijndael which has a fixed blocksize of 128 bits, and a key size of 128, 192, or 256bits...

- an optional IV (initialization vector). If you use a key only once, all is good. Using the same key all the time (e.g. for years) is a security leak! Why? AES uses fixed blocks to organize the encryption. If you encrypt with a key, the sequence of the blocks is always the same without IV. If you encrypt word documents, a lot of may start with "Dear Sir or Madam...." (or similar) the position of this text is always the same. Knowing this makes it very easy to hack your key. Using an IV "randomizes" these blocks which makes it safe again.

This only works when you use ONE key for a long time and use a different IV for every message you encrypt. By the way the IV MUST have a length of 128 Bits (or 16 Bytes). So how does this work? How do I transfer the IV to the receipient of the message?

Easy: Keep the key secret. The IV can be transfered non-encrypted (e.g. combine the 16 Bytes long IV and the encrypted message. Get the first 16 Bytes when you receive the message and the rest is the encrypted message. Any method will do.

An initialization vector (IV) is an arbitrary number that can be used along with a secret key for data encryption. This number, also called a nonce, is employed only one time in any session. ... The IV length is usually comparable to the length of the encryption key or block of the cipher in use.

Examples:

Generate random IV (always 128 Bits or 16 Bytes long!). I use just letters and numbers as a string (which will be later converted to Bytes). You can use any Byte you want but be careful when you send the IV because when it contains other chars it may be concerted (URL encoded, special chars, etc.) which causes problems.

B4X:
Sub GenerateIV As String
    Dim PWC As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    Dim IV As String
    For i=0 To 15
        IV=IV & PWC.CharAt(Rnd(0,PWC.Length))
    Next
    Return IV
End Sub

Encrypt (AES-256) = 32 Chars/Bytes long (32 * 8 = 256)

Input=Text to encrypt
IV=The just generated IV
pass=your key (exact 32 Chars long here for AES-256, 16 for AES-128)

B4X:
Sub AES_Encrypt(input As String, IV As String, pass As String) As String

    Dim inputB() As Byte = input.GetBytes("UTF8")
    Dim passB() As Byte = pass.GetBytes("UTF8")
    Dim IVb() As Byte = IV.GetBytes("UTF8")
  
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES") 'Yes, AES only
    kg.KeyFromBytes(passB)
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Encrypt(inputB, kg.Key, True)
 
    Return SU.EncodeBase64(datas)
End Sub

The resut is a Base64 encoded string

Decrypt:

Use same Key AND IV you used for encryption!

Input: Base64 encoded string
IV: See encryption
Pass: See encryption

B4X:
Sub AES_Decrypt(input As String, IV As String, pass As String) As String

    Dim inputB() As Byte = SU.DecodeBase64(input)
    Dim passB() As Byte = pass.GetBytes("UTF8")
    Dim IVb() As Byte = IV.GetBytes("UTF8")
  
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES")
    kg.KeyFromBytes(passB)
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Decrypt(inputB, kg.Key, True)
 
    Return BytesToString(datas, 0, datas.Length, "UTF8")

End Sub

PHP-Side: https://www.b4x.com/android/forum/threads/b4x-php-compatibility-thread.84473/#post-616967

Discussion on Stackoverflow: https://stackoverflow.com/questions/9049789/aes-encryption-key-versus-iv

Wiki: https://en.wikipedia.org/wiki/Initialization_vector

Lessons learned:

1. Always use an IV!
2. Never re-use the same IV again!
3. For every message generate a new IV!
4. Update your apps (for me: I need to update my apps and some of my tutorials :))
5. Don't panic if your backend-server uses https:// (RSA) (it's quite safe)
6. Not using https:// is VERY unsafe
5. Happy en-/decryption!
 

JohnC

Expert
Licensed User
Longtime User
Thank you for taking the time to share your experiences - I am sure it will save many of us a lot of time :)
 

fbritop

Active Member
Licensed User
Longtime User
@KMatle great piece of code!. Although I'm having some issues.

I try your functions with:

B4X:
Sub EncryptText(text As String) As String
    Dim inputB() As Byte = text.GetBytes("utf8")
    Dim passB() As Byte = "1234567812345678".GetBytes("utf8")
    Dim IVb() As Byte ="1234567812345678".GetBytes("utf8")
    Dim  su As StringUtils
    Dim kg As KeyGenerator
    Dim C As Cipher
    kg.Initialize("AES")
    kg.KeyFromBytes(passB)
     C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
    Dim datas() As Byte = C.Encrypt(inputB, kg.Key, True)
    Return su.EncodeBase64(datas)
End Sub

Sub DecryptText(text As String) As String
    Dim inputB() As Byte = text.GetBytes("utf8")
    Dim passB() As Byte = "1234567812345678".GetBytes("utf8")
    Dim IVb() As Byte ="1234567812345678".GetBytes("utf8")
    Dim kg As KeyGenerator
    Dim C As Cipher
    kg.Initialize("AES")
    kg.KeyFromBytes(passB)
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
    Dim datas() As Byte = C.Decrypt(inputB, kg.Key, True)
    Return BytesToString(datas, 0, datas.Length, "UTF8")
End Sub


It outputs correctly when I encrypt, but when I try do decrypt then encrypted, it outputs and error:
javax.crypto.IllegalBlockSizeException: error:1e00007b:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH for the line that does the "C.Decrypt" function.

The call is made like this:
B4X:
dim qrCode as String=EncryptText("TEST1")
Log(qrCode)
'-->Prints MwokvR3ICrCKK4deYqssLQ==
Log(DecryptText(qrCode))<-- Error



Last weekend I migrated my XAMPP test environment (Apache/PHP/MySql). It came with PHP 7.x. Yesterday I wanted to test one of my Apps using AES and Mcrypt for it in PHP but it didn't work no more. I noticed that MCrypt was deprecated anyway and even was removed due to security and other issues from PHP 7.x. So I starded searching for an alternative for PHP which is matching B4x (I use agraham's Encryption lib: https://www.b4x.com/android/forum/threads/base64-and-encryption-library.6839/#content)

Soon I came across OpenSSL which I use for RSA encryption on the PHP side for some years and tried to migrate my code to it. Until then I was using AES-128 Bit. After reading some articles I learned some new things which I want to write down here. Using AES is quite simple if you know the details. Not knowing these details makes your encryption usafe.

Even on my production servers which use RSA (https://) I use AES addtionally and encrypt ALL data I send/receive to/from them.

However: AES uses

- a key (the data is encrypted with this key). The key is either 128, 192 or 256 Bits long (aka AES-128, AES-192 and AES-256). As a string it has a length of 16,24 or 32 chars/bytes. So noch chance to use other lengths



- an optional IV (initialization vector). If you use a key only once, all is good. Using the same key all the time (e.g. for years) is a security leak! Why? AES uses fixed blocks to organize the encryption. If you encrypt with a key, the sequence of the blocks is always the same without IV. If you encrypt word documents, a lot of may start with "Dear Sir or Madam...." (or similar) the position of this text is always the same. Knowing this makes it very easy to hack your key. Using an IV "randomizes" these blocks which makes it safe again.

This only works when you use ONE key for a long time and use a different IV for every message you encrypt. By the way the IV MUST have a length of 128 Bits (or 16 Bytes). So how does this work? How do I transfer the IV to the receipient of the message?

Easy: Keep the key secret. The IV can be transfered non-encrypted (e.g. combine the 16 Bytes long IV and the encrypted message. Get the first 16 Bytes when you receive the message and the rest is the encrypted message. Any method will do.



Examples:

Generate random IV (always 128 Bits or 16 Bytes long!). I use just letters and numbers as a string (which will be later converted to Bytes). You can use any Byte you want but be careful when you send the IV because when it contains other chars it may be concerted (URL encoded, special chars, etc.) which causes problems.

B4X:
Sub GenerateIV As String
    Dim PWC As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    Dim IV As String
    For i=0 To 15
        IV=IV & PWC.CharAt(Rnd(0,PWC.Length))
    Next
    Return IV
End Sub

Encrypt (AES-256) = 32 Chars/Bytes long (32 * 8 = 256)

Input=Text to encrypt
IV=The just generated IV
pass=your key (exact 32 Chars long here for AES-256, 16 for AES-128)

B4X:
Sub AES_Encrypt(input As String, IV As String, pass As String) As String

    Dim inputB() As Byte = input.GetBytes("UTF8")
    Dim passB() As Byte = pass.GetBytes("UTF8")
    Dim IVb() As Byte = IV.GetBytes("UTF8")
 
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES") 'Yes, AES only
    kg.KeyFromBytes(passB)
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Encrypt(inputB, kg.Key, True)
 
    Return SU.EncodeBase64(datas)
End Sub

The resut is a Base64 encoded string

Decrypt:

Use same Key AND IV you used for encryption!

Input: Base64 encoded string
IV: See encryption
Pass: See encryption

B4X:
Sub AES_Decrypt(input As String, IV As String, pass As String) As String

    Dim inputB() As Byte = SU.DecodeBase64(input)
    Dim passB() As Byte = pass.GetBytes("UTF8")
    Dim IVb() As Byte = IV.GetBytes("UTF8")
 
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES")
    kg.KeyFromBytes(passB)
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Decrypt(inputB, kg.Key, True)
 
    Return BytesToString(datas, 0, datas.Length, "UTF8")

End Sub

PHP-Side: https://www.b4x.com/android/forum/threads/b4x-php-compatibility-thread.84473/#post-616967

Discussion on Stackoverflow: https://stackoverflow.com/questions/9049789/aes-encryption-key-versus-iv

Wiki: https://en.wikipedia.org/wiki/Initialization_vector

Lessons learned:

1. Always use an IV!
2. Never re-use the same IV again!
3. For every message generate a new IV!
4. Update your apps (for me: I need to update my apps and some of my tutorials :))
5. Don't panic if your backend-server uses https:// (RSA) (it's quite safe)
6. Not using https:// is VERY unsafe
5. Happy en-/decryption!
 

OliverA

Expert
Licensed User
Longtime User
Dim inputB() As Byte = text.GetBytes("utf8")
This line is wrong. Remember, the input string to your decrypt function is a Base64 encoded string. You have to decode the string from Base64, not just turn that string into a Byte array. Look at the AES_Decrypt method again.

Note/update: Why not just use B4XEnryption (https://www.b4x.com/android/forum/threads/b4xencryption.48177/) instead of trying to roll your own. It takes care of generating a random IV (it really needs to be. If you always use a fixed IV, you may as well not use one) and it salts the password, without you having to lift a finger. Plus it works with B4J, B4A and B4i.
 

Yvon Steinthal

Active Member
Licensed User
I've been using this successfully between B4A and my PHP server, however i dont know where to start for my B4i App that needs to talk the same way to the server...
Any help?

EDIT: Found my solution:

B4X:
Public Sub AES_Encrypt(input As String, IV As String, pass As String) As String
   
    Dim su As StringUtils
    Dim bc As ByteConverter
   
    Dim inputB() As Byte = input.GetBytes("UTF-8")
    Dim passB() As Byte = pass.GetBytes("UTF-8")
    Dim IVb() As Byte = IV.GetBytes("UTF-8")

    Dim C As Cipher
    Dim datas() As Byte = c.Encrypt2(inputB, passB, "AES", IVb, c.OPTION_PKCS7Padding )
    'Log(su.EncodeBase64(datas))
    Return su.EncodeBase64(datas)
   
End Sub
 
Last edited:

Addo

Well-Known Member
Licensed User
Python Version

Python:
from Crypto.Cipher import AES
from base64 import b64decode, b64encode

KeyText = 'Your Key'
ivtext = 'Your IV'

# Block size and PKCS5 Padding
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-s[-1]]



class B4xCrypto:
    def __init__(self):
        self.key = KeyText.encode('utf8')  # key
        self.iv = ivtext.encode('utf8')
        self.mode = AES.MODE_CBC
        self.bs = AES.block_size
        
    def encrypt(self, Textraw):
        generator = AES.new(self.key, self.mode, self.iv)
        Textraw = pad(Textraw)
        print(self.bs)
        encrypted_text = generator.encrypt(Textraw.encode('utf8'))
        return b64encode(encrypted_text).decode('utf8')

    def decrypt(self, Textraw):
        encrypted_text = b64decode(Textraw)
        generator = AES.new(self.key, self.mode,self.iv)
        decrypted_text = generator.decrypt(encrypted_text)
        outputText = unpad(decrypted_text).decode('utf8')
        return outputText
 
Top