B4J Code Snippet [B4x] AES-256 encryption with salt and iv (works with all platforms like php, .net, etc.)

This example is based on agrahams encryption library: Encryption Lib

1. Generate a 32 byte pw (just call the sub) and store it
2. Encrypt the data by calling AES_Encrypt. Return is a byte array or a base64 encoded string
3. Salt is a random value which is added at the beginning of the encrypted string. It is a good practice to add some random bytes to a message. A hacker doesn't know how long this "salt" is. Could be 16 or 16K of bytes.
4. IV is public and random, too and is used to scramble the order of the blocks (AES is a block cypher method). With this method you always get different byte orders with the same message (which is good as in encryption one does not want repetitive data sequences at it is seen a problematic). Like the Emigma device which was hacked in WW2 because almost all messages started with a standard weather report using the same sentences/words.
5. Use RSA to exchange the key/pw (so you can exchange it like SSL does).



B4X:
Public Sub AES_Encrypt(data() As Byte, passB() As Byte, ReturnB64String As Boolean) As Object
    
    Dim SaltB() As Byte = GenerateSalt(32)
    Dim IVb() As Byte = GenerateIV
    
    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(data, kg.Key, True)
    Dim SaltIVMessage(SaltB.Length + datas.Length + IVb.Length) As Byte = AddSaltIVMessage(SaltB,IVb,datas)
    
    If ReturnB64String Then
        Return SU.EncodeBase64(SaltIVMessage)
    Else
        Return SaltIVMessage
    End If   
End Sub

'Input is a Base664 encoded string or a byte array, output a string or byte array
'Salt (32) and IV(16) is added at the beginning
Public Sub AES_Decrypt(data() As Byte,  passb() As Byte, ReturnString As Boolean) As Object

    
    Dim m As Map = SplitSaltIVMessage(data)
    
    Dim IVb() As Byte = m.Get("iv")
    Dim MessageB() As Byte = m.Get("message")
    
    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(MessageB, kg.Key, True)
 
     If ReturnString Then
        Return BytesToString(datas, 0, datas.Length, "UTF8")
    Else
        Return datas
    End If
End Sub

Public Sub GetMessageSaltIVFromEncryptedMessage(EncryptedMessageAsBytes() As Byte) As Map
    Dim m As Map = SplitSaltIVMessage(EncryptedMessageAsBytes)
    
    Return m
End Sub

Private Sub AddSaltIVMessage (Salt() As Byte,IV() As Byte, Message () As Byte) As Byte()
    
    Dim SaltIVMessageBytes (Salt.Length+ IV.Length + Message.Length) As Byte
    BC.ArrayCopy(Salt,0,SaltIVMessageBytes,0,32)
    BC.ArrayCopy(IV,0,SaltIVMessageBytes,32,16)
    BC.ArrayCopy(Message,0,SaltIVMessageBytes,48,Message.Length)
    
    Return SaltIVMessageBytes
End Sub

Private Sub SplitSaltIVMessage (SaltIvMessage () As Byte) As Map
    
    Dim Salt(32),IV (16), Message(SaltIvMessage.length-48) As Byte
    
    BC.ArrayCopy(SaltIvMessage,0,Salt,0,32)
    BC.ArrayCopy(SaltIvMessage,32,IV,0,16)
    BC.ArrayCopy(SaltIvMessage,48,Message,0,Message.Length)
    
    Dim m As Map=CreateMap("salt":Salt,"iv":IV,"message":Message)
        
    Return m
End Sub

Private Sub GenerateSalt (l As Int) As Byte()
    Dim SaltB(l) As Byte
    For i=0 To l-1
        SaltB(i)=Rnd(0,256)-127
    Next
    Return SaltB
End Sub

Public Sub GeneratePW32Bytes As Byte()
    Dim pw(32) As Byte
    For i=0 To 31
        pw(i)=Rnd(0,256)-127
    Next
    Return pw
End Sub

Private Sub GenerateIV As Byte()
    
    Dim IVB(16) As Byte
    For i=0 To 15
        IVB(i)=Rnd(0,256)-127
    Next
    
    Return IVB
        
End Sub

PHP-Example:

B4X:
function aes_encrypt($string, $pw)
    {
    $output = false;
    $pw=hash('sha256',$pw,true);
    $encrypt_method = "AES-256-CBC";
    $IV=openssl_random_pseudo_bytes(16, $securityok);
    $Salt=openssl_random_pseudo_bytes(32, $securityok);
    $output = openssl_encrypt($string, $encrypt_method, $pw, 0,$IV);
    $output=Base64_encode($Salt.$IV.Base64_decode($output));
    //$output is base64 encoded automatically!
    return $output;
    }   
function aes_decrypt($string, $pw)
    {
    $pw=hash('sha256',$pw,true);
    $output = false;
    $encrypt_method = "AES-256-CBC";
    $total=base64_decode($string);
    $salt=substr($total, 0, 32);
    $iv=substr($total, 32, 16);
    $string=Base64_Encode(substr($total, 48, strlen($total)-48));
    $output = openssl_decrypt($string, $encrypt_method, $pw, 0,$iv);
    //$string must be base64 encoded!
    return $output;
    }
 

arman98

New Member
Please write a sample code to create, What code should I write in Activity_create ?
 
Last edited:

KMatle

Expert
Licensed User
Longtime User
So here's a small example. It works in B4J and B4A.

Libraries used:
1622797213200.png


Notes:

- the goal is to send/receive a simple string in the end using the POSTSTRING method
- using strings is very easy but comes unhandy when you need structured data
- here we use maps (could be a list with maps) which are converted to JSON strings and back
- PHP can handle lists/maps very easy. Here these types are just associative arrays (key -> value)
- I've added some subs to convert "all" types to strings

- like any encryption, AES uses bytes to en-/decrypt
- getting bytes from strings is easy, too by using StringName.GetBytes("UTF8")
- AES256 uses a fixes length password of 32 bytes (= 256 bits), AES128 would use 16 (= 128 bits)
- the IV is randomized with every message (= some sort of changing the sort order of the data = same data will look different every time)
- a "salt" is added, too (here: 32 bytes. Can be longer) to randomize it more
- an encrypted message is always a multiple of 16 bytes long (AES = blockcypher). So "hello" will be padded to 16 bytes.

- the message structure is: salt + iv + message
- salt and iv are "public"

- on the PHP side we use OpenSSL
- the methods need a Base64 encoded string (= encrypted bytes) as an input or output

Workflow:

- get the bytes of the string which you want to encrypt (or use a byte array directly if you want to encrypt just bytes)
- hash the password to 32 bytes (see the sub) or use random 32 bytes (which can be converted to a Base64 string to handle it better)
- call the encryption sub
- Salt and IV are added automatically
- Send the data (here a map) to the receiver (here PHP, can be any other platform, too)
- the receiver has to know the SAME password and the position/length of salt and iv
- salt and iv are removed from the message
- the message can now be decrypted
- on some platforms you need to pad the message on your own (e.g. ESP32). Search for my padding example

Pro/paranoia tips:

- add another salt before encryption (e.g. 64 bytes)
- mix the data bytes with the salt (odd/even)
- as both sides need the same password, use RSA to exchange a different aes password for every client/instance/session (SSL does quite the same)
- encrypt it twice with different passwords


PHP (put it in your htdocs folder)

PHP:
<?php

$aespw=hash('sha256','#$%&12890GHbfewiwegbweoegn',true); //hash string pw to 32 bytes (=256 bits) as bytes (true=binary=bytes)

$jsonstring = file_get_contents("php://input"); //get the string or whatever you send via poststring

$mymap = array(); //define an array (which is the map we send from B4x)
$mymap=json_decode($jsonstring, true); //the JSONencoded map from B4x is decoded to an associative array which is quite the same in php

$Action=$mymap['Action']; //get the value from the map by the key (like a map) so we get the method (ecnrypt or decrypt)

switch ($Action) //Select case... in B4x
    {
        
    Case "AES256":
        $decrypted=AES256_decrypt($mymap['b4xencrypted'],$aespw);//decrypt the message sent from B4x
        $encrypted=AES256_encrypt('This message was encrypted in PHP',$aespw );//encrypt a new message in PHP (to send it back)
        
        //we send back the decrypted message (cleartext) and a new message encrypted via php
        //of course we would never send back cleatext (just to proof the decryption)
        $res=json_encode(array('AESDecrypt' => 'ok','b4xdecrypted' => $decrypted,'phpencrypted' => $encrypted)); //this is an associative array or a map
        exit($res);
        break;


default:
        print json_encode ("Error: Function not defined (" . $action . ")");
    }   


function AES256_Encrypt($string, $pw)
{
    $encrypt_method = "AES-256-CBC";
    $IV=openssl_random_pseudo_bytes(16, $securityok);//generate a radom IV
    $Salt=openssl_random_pseudo_bytes(32, $securityok);//generate a random salt
    $enc = openssl_encrypt($string, $encrypt_method, $pw, 0,$IV);//encrypt with the given IV       
    $enc=Base64_encode($Salt.$IV.Base64_decode($enc));//add salt, iv and the message to a Base64 string (OPENSSL encrypt returns a Base64 string so we have to decode it first)
    return $enc;
}

function AES256_Decrypt($string, $pw)
{
    $dec = false;
    $encrypt_method = "AES-256-CBC";
    $total=base64_decode($string); //salt + iv + message
    $salt=substr($total, 0, 32);//extract the salt
    $iv=substr($total, 32, 16);//ectract iv
    $string=Base64_Encode(substr($total, 48, strlen($total)-48));//extract the message. Must be Base64 encoded for OpenSSL decrypt
    $dec = openssl_decrypt($string, $encrypt_method, $pw, 0,$iv);//decrypt...
    return $dec;
}

?>
 

Attachments

  • AES256EncryptionPHP.zip
    2.4 KB · Views: 443

SMOOTSARA

Active Member
Licensed User
Longtime User
So here's a small example. It works in B4J and B4A.

Libraries used:
View attachment 114466

Notes:

- the goal is to send/receive a simple string in the end using the POSTSTRING method
- using strings is very easy but comes unhandy when you need structured data
- here we use maps (could be a list with maps) which are converted to JSON strings and back
- PHP can handle lists/maps very easy. Here these types are just associative arrays (key -> value)
- I've added some subs to convert "all" types to strings

- like any encryption, AES uses bytes to en-/decrypt
- getting bytes from strings is easy, too by using StringName.GetBytes("UTF8")
- AES256 uses a fixes length password of 32 bytes (= 256 bits), AES128 would use 16 (= 128 bits)
- the IV is randomized with every message (= some sort of changing the sort order of the data = same data will look different every time)
- a "salt" is added, too (here: 32 bytes. Can be longer) to randomize it more
- an encrypted message is always a multiple of 16 bytes long (AES = blockcypher). So "hello" will be padded to 16 bytes.

- the message structure is: salt + iv + message
- salt and iv are "public"

- on the PHP side we use OpenSSL
- the methods need a Base64 encoded string (= encrypted bytes) as an input or output

Workflow:

- get the bytes of the string which you want to encrypt (or use a byte array directly if you want to encrypt just bytes)
- hash the password to 32 bytes (see the sub) or use random 32 bytes (which can be converted to a Base64 string to handle it better)
- call the encryption sub
- Salt and IV are added automatically
- Send the data (here a map) to the receiver (here PHP, can be any other platform, too)
- the receiver has to know the SAME password and the position/length of salt and iv
- salt and iv are removed from the message
- the message can now be decrypted
- on some platforms you need to pad the message on your own (e.g. ESP32). Search for my padding example

Pro/paranoia tips:

- add another salt before encryption (e.g. 64 bytes)
- mix the data bytes with the salt (odd/even)
- as both sides need the same password, use RSA to exchange a different aes password for every client/instance/session (SSL does quite the same)
- encrypt it twice with different passwords


PHP (put it in your htdocs folder)

PHP:
<?php

$aespw=hash('sha256','#$%&12890GHbfewiwegbweoegn',true); //hash string pw to 32 bytes (=256 bits) as bytes (true=binary=bytes)

$jsonstring = file_get_contents("php://input"); //get the string or whatever you send via poststring

$mymap = array(); //define an array (which is the map we send from B4x)
$mymap=json_decode($jsonstring, true); //the JSONencoded map from B4x is decoded to an associative array which is quite the same in php

$Action=$mymap['Action']; //get the value from the map by the key (like a map) so we get the method (ecnrypt or decrypt)

switch ($Action) //Select case... in B4x
    {
       
    Case "AES256":
        $decrypted=AES256_decrypt($mymap['b4xencrypted'],$aespw);//decrypt the message sent from B4x
        $encrypted=AES256_encrypt('This message was encrypted in PHP',$aespw );//encrypt a new message in PHP (to send it back)
       
        //we send back the decrypted message (cleartext) and a new message encrypted via php
        //of course we would never send back cleatext (just to proof the decryption)
        $res=json_encode(array('AESDecrypt' => 'ok','b4xdecrypted' => $decrypted,'phpencrypted' => $encrypted)); //this is an associative array or a map
        exit($res);
        break;


default:
        print json_encode ("Error: Function not defined (" . $action . ")");
    }  


function AES256_Encrypt($string, $pw)
{
    $encrypt_method = "AES-256-CBC";
    $IV=openssl_random_pseudo_bytes(16, $securityok);//generate a radom IV
    $Salt=openssl_random_pseudo_bytes(32, $securityok);//generate a random salt
    $enc = openssl_encrypt($string, $encrypt_method, $pw, 0,$IV);//encrypt with the given IV      
    $enc=Base64_encode($Salt.$IV.Base64_decode($enc));//add salt, iv and the message to a Base64 string (OPENSSL encrypt returns a Base64 string so we have to decode it first)
    return $enc;
}

function AES256_Decrypt($string, $pw)
{
    $dec = false;
    $encrypt_method = "AES-256-CBC";
    $total=base64_decode($string); //salt + iv + message
    $salt=substr($total, 0, 32);//extract the salt
    $iv=substr($total, 32, 16);//ectract iv
    $string=Base64_Encode(substr($total, 48, strlen($total)-48));//extract the message. Must be Base64 encoded for OpenSSL decrypt
    $dec = openssl_decrypt($string, $encrypt_method, $pw, 0,$iv);//decrypt...
    return $dec;
}

?>


Hi KMatle?
Thank you for sharing this practical example?
Some time ago, I asked a question in this regard.
Can you take a look at it?
https://www.b4x.com/android/forum/threads/encryption-functions-hmacsha256.134941/
Thank you for my guidance
 

Addo

Well-Known Member
Licensed User
Longtime User
Python Version

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


KeyText = 'Your Key'

# Block Size
BS = AES.block_size

class B4xCrypto:
    def __init__(self, passtext):
        self.key = passtext.encode('utf8')  # key
        self.mode = AES.MODE_CBC
        self.bs = BS


    def pad(self, bText):
        TextLen = len(bText)
        number_of_bytes_to_pad = BS - TextLen % BS
        a_string = chr(number_of_bytes_to_pad)
        padding_bText = number_of_bytes_to_pad * a_string
        padded_plain_text = bText + padding_bText
        return padded_plain_text

    def unpad(self, bText):
        TextLen = len(bText)
        stripsize = bText[TextLen-1]
        outputdata = bText[0:-stripsize]
        return outputdata
    
    def encrypt(self, Textraw):
        Ivb = Random.new().read(16)
        salt = Random.new().read(32)
        Textraw = self.pad(Textraw)
        generator = AES.new(self.key, self.mode, Ivb)
        encrypted_text = generator.encrypt(Textraw.encode('utf8'))
        outputText = b64encode(salt+Ivb+encrypted_text).decode('utf8')
        return outputText

    def decrypt(self, Textraw):
        encrypted_text = b64decode(Textraw)
        salt = encrypted_text[0:32]
        Ivb = encrypted_text[32:16:-1]
        Msg = encrypted_text[32:] #Extract the message with the victor length Without salt Since this is mandatory for decryption
        generator = AES.new(self.key, self.mode, Ivb)
        decrypted_text = generator.decrypt(Msg)
        decrypted_text = decrypted_text[16:] #Now we will have the message without victor to correctly decoding it.
        outputText = self.unpad(decrypted_text).decode('utf8')
        return outputText
 

Hamied Abou Hulaikah

Well-Known Member
Licensed User
Longtime User
i modified the code to encrypt & decrypt without salt, encrypt worked, but decrypt through error: Input length must be multiple of 16 when decrypting with padded cipher
B4X:
Public Sub AES_Encrypt(data As String, secretkey As String) As String
  
    Dim iv As String= RandomIVString(16)
    Dim IVb() As Byte = iv.GetBytes("UTF8")
     
    Dim kg As KeyGenerator
    Dim C As Cipher
    Dim SU As StringUtils
  
    kg.Initialize("AES")
    kg.KeyFromBytes(secretkey.GetBytes("UTF8"))
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Encrypt(data.GetBytes("UTF8"), kg.Key, True)
  
    Return iv & SU.EncodeBase64(datas)
  
End Sub


Public Sub AES_Decrypt(data As String,  secretkey As String) As String
  
    Log(data)
    Dim iv As String=data.SubString2(0,16)
    Log("iv: " & iv)
    Dim msg As String=data.Replace(iv,"")
    Log(msg)
  
    Dim IVb() As Byte = iv.GetBytes("UTF8")
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES")
    kg.KeyFromBytes(secretkey.GetBytes("UTF8"))
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Decrypt(msg.GetBytes("UTF8"), kg.Key, True)
  
    Dim SU As StringUtils
    Return SU.EncodeBase64(datas)
  
End Sub

Private Sub RandomIVString(length As Int) As String
    Dim abc As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    Dim randomstr As String = ""
    For i = 0 To length - 1
        randomstr = randomstr & (abc.CharAt(Rnd(0,abc.Length)))
    Next
    Return randomstr
End Sub

Private Sub Button1_Click
    Dim keyy As String = "j4s5o1v1r2p4n7l6y7u2h5e1x4goswqm"
    Dim a As String=AES_Encrypt("hello hamied",keyy)
    Log(AES_Decrypt(a,keyy))
End Sub
 

Attachments

  • aestest.zip
    3.5 KB · Views: 238
Last edited:

Hamied Abou Hulaikah

Well-Known Member
Licensed User
Longtime User
[SOLVED]
correct code for reference:
B4X:
Public Sub AES_Encrypt(data As String, secretkey As String) As String
   
    Dim SU As StringUtils
   
    Dim iv As String= RandomIVString(16)
    Dim IVb() As Byte = iv.GetBytes("UTF8")
    Dim kg As KeyGenerator
    Dim C As Cipher
    Dim SU As StringUtils
   
   
    kg.Initialize("AES")
    kg.KeyFromBytes(secretkey.GetBytes("UTF8"))
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Encrypt(data.GetBytes("UTF8"), kg.Key, True)
   
    Return iv & SU.EncodeBase64(datas)
   
End Sub


Public Sub AES_Decrypt(data As String,  secretkey As String) As String
   
    Dim SU As StringUtils
   
    Dim iv As String = data.SubString2(0,16)   
    Dim msg As String=data.Replace(iv,"")
   
    Dim IVb() As Byte = iv.GetBytes("UTF8") 'SU.DecodeBase64(iv)
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES")
    kg.KeyFromBytes(secretkey.GetBytes("UTF8"))
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector = IVb
 
    Dim datas() As Byte = C.Decrypt(SU.DecodeBase64(msg), kg.Key, True)
   
    Dim SU As StringUtils
    Return BytesToString(datas,0,datas.Length,"UTF8")
   
End Sub

Private Sub RandomIVString(length As Int) As String
    Dim abc As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    Dim randomstr As String = ""
    For i = 0 To length - 1
        randomstr = randomstr & (abc.CharAt(Rnd(0,abc.Length)))
    Next
    Return randomstr
   
End Sub

   

End Sub
 
Top