Android Question Having trouble implementing mysql AES_Encrypt/Aes_Decrypt compatible functions

toby

Well-Known Member
Licensed User
Longtime User
I'm new to encryption, btw, and I want to be able to compare encrypted mysql data on the fly while executing a select query like

B4X:
SELECT * from myTable WHERE
encrytedField=selectedEncryptedValue

or

SELECT * from myTable WHERE
aes_decrypt(encrytedField, '123')=selectedValue

Therefore I need to implement Encrypt() function whose result can be decrypted by mysql's aes_decrypt(); the other way around is desirable but not required as this moment.

My code caused an IV related error (see logs below) while trying to encrypt. Also I don't know how to ignore Initialization vector.
B4A encrypt and decrypt functions:
Sub Class_Globals
    Dim mysqlPwd As String="123"

    Dim bc As ByteConverter
    Dim su As StringUtils
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   
End Sub


'mysql 8: SELECT aes_encrypt("abc", "123"), length(aes_encrypt("abc", "123")), aes_decrypt(aes_encrypt("abc", "123"), '123');
'result: 0x911ff5b9a15aae9b52e7e9fde75315b1, 16, abc

Public Sub Mysql_Encrypt(msg As String) As String
    Dim data() As Byte=msg.GetBytes("UTF8") 'convert string to bytes
    Dim md As MessageDigest
    Dim passB() As Byte= md.GetMessageDigest(mysqlPwd.GetBytes("UTF8"),"SHA-256")
   
   
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES")
    kg.KeyFromBytes(passB)
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector =passB
   
    Dim datas() As Byte = C.Encrypt(data, kg.Key, True) '<-causes error
   
    Dim result As String= bc.HexFromBytes(datas)
    Return result
End Sub

Public Sub Mysql_Decrypt(msg As String) As String
    Dim data() As Byte=su.DecodeBase64(msg) 'convert string to bytes
    Dim md As MessageDigest

    Dim passB() As Byte= md.GetMessageDigest(mysqlPwd.GetBytes("UTF8"),"SHA-256")
           
    Dim kg As KeyGenerator
    Dim C As Cipher
 
    kg.Initialize("AES")
    kg.KeyFromBytes(passB)
 
    C.Initialize("AES/CBC/PKCS5Padding")
    C.InitialisationVector =passB
 
    Dim datas() As Byte = C.Decrypt(data, kg.Key, True)
    Dim result As String=bc.HexFromBytes(datas)
    Return result
End Sub

I would greatly appreciate it if someone could give me some hint on how to fix the error and make those two functions work.



** Activity (main) Resume **
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
Error occurred on line: 32 (mysql_aes)
java.security.InvalidAlgorithmParameterException: expected IV length of 16 but was 32
at com.android.org.conscrypt.OpenSSLEvpCipher.engineInitInternal(OpenSSLEvpCipher.java:94)
at com.android.org.conscrypt.OpenSSLCipher.engineInit(OpenSSLCipher.java:308)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2980)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2891)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2796)
at javax.crypto.Cipher.chooseProvider(Cipher.java:773)
at javax.crypto.Cipher.init(Cipher.java:1288)
at javax.crypto.Cipher.init(Cipher.java:1223)
at anywheresoftware.b4a.agraham.encryption.CipherWrapper.doFinal(CipherWrapper.java:136)
at anywheresoftware.b4a.agraham.encryption.CipherWrapper.Encrypt(CipherWrapper.java:160)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:348)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
at anywheresoftware.b4a.debug.Debug.delegate(Debug.java:262)
at b4a.example.mysql_aes._mysql_encrypt(mysql_aes.java:45)
at b4a.example.b4xmainpage._b4xpage_created(b4xmainpage.java:59)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:318)
at anywheresoftware.b4a.debug.Debug.CallSubNew2(Debug.java:285)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.keywords.Common.CallSubDebug2(Common.java:1055)
at b4a.example.b4xpagesmanager._createpageifneeded(b4xpagesmanager.java:1060)
at b4a.example.b4xpagesmanager._showpage(b4xpagesmanager.java:417)
at b4a.example.b4xpagesmanager._addpage(b4xpagesmanager.java:245)
at b4a.example.b4xpagesmanager._addpageandcreate(b4xpagesmanager.java:259)
at b4a.example.b4xpagesmanager._initialize(b4xpagesmanager.java:165)
at b4a.example.main._activity_create(main.java:415)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:351)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:144)
at b4a.example.main.afterFirstLayout(main.java:105)
at b4a.example.main.access$000(main.java:17)
at b4a.example.main$WaitForLayout.run(main.java:83)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8550)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
 

Attachments

  • Project.zip
    14.9 KB · Views: 57
Last edited:

toby

Well-Known Member
Licensed User
Longtime User
I've found a solution at stackoverflow. Could some encryption expert help me translate the java code into B4A one?

TIA

1. key generation:
B4X:
/**
 * Use password to generate a MySQL AES symmetric key
 * @param passwd Password String to use.
 * @param keyLength Must be evenly divisible by 8.
 * @return Key for use with MySQL AES encrypt/decrypt fuctions.
 */
public static KeyParameter getMySqlAESPasswdKey(String passwd, int keyLength) {
    byte[] pword = passwd.getBytes();
    byte[] rawKey = new byte[keyLength/8];
    int j = 0;
    for (int i = 0; i < pword.length; i++, j++) {

        if(j==rawKey.length) {
            j = 0;
        }
        rawKey[j] = pword[i];
    }

    return new KeyParameter(rawKey);
}

2. Pad Key with BouncyCastle's PaddedBufferedBlockCipher

3. Encrypt and Decrypt functions
B4X:
/**
 * Password based encryption using AES with MySql style key generation.
 * @param toEncrypt Unencrypted byte array.
 * @param key A KeyParameter generated with the getMySqlAESPasswdKey() method.
 * @return Encrypted byte array.
 * @throws InvalidCipherTextException If provided key cannot be used with this method on the provided data.
 */
public static byte[] mysqlAesPasswdEncrypt (byte [] toEncrypt, KeyParameter key) throws InvalidCipherTextException {
    BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new AESFastEngine());

    cipher.init(true, key);
    byte[] result = new byte[cipher.getOutputSize(toEncrypt.length)];
    int len = cipher.processBytes(toEncrypt, 0, toEncrypt.length, result, 0);
    cipher.doFinal(result, len);
    return result;
}

/**
 * Password based decryption using AES with MySql style key generation.
 * @param toDecrypt Encrypted byte array.
 * @param key A KeyParameter generated with the getMySqlAESPasswdKey() method.
 * @return Unencrypted byte array.
 * @throws InvalidCipherTextException If provided key cannot be used with this method on the provided data.
 */
public static byte[] mysqlAesPasswdDecrypt (byte [] toDecrypt, KeyParameter key) throws InvalidCipherTextException {
    BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new AESFastEngine());

    cipher.init(false, key);
    byte[] result = new byte[cipher.getOutputSize(toDecrypt.length)];
    int len = cipher.processBytes(toDecrypt, 0, toDecrypt.length, result, 0);
    cipher.doFinal(result, len);
    return stripTrailingZeros(result);
}

/**
 * Strip trailling zeros from the end of decrypted byte arrays.
 * @param data Data to strip.
 * @return Stripped data.
 */
public static byte[] stripTrailingZeros(byte[] data) {
    int lastData = data.length-1;
    for (int i = data.length-1; i >= 0; i--) {
        if(data[i]!=(byte)0) {
            lastData = i;
            break;
        }
    }

    byte[] data2 = new byte[lastData+1];
    System.arraycopy(data, 0, data2, 0, lastData+1);
    return data2;
}
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
This should work:
 
Upvote 0

KMatle

Expert
Licensed User
Longtime User
The error message: Expected IV length of 16 instead of 32

- IV is always 16 bytes long (not 32). Usually it is generated per message and added to the data (it's public and "mixes" the data to prevent analysing of "patterns" to get hints for the pw)
- PW is either exactly 16 bytes (for 128 bit -> 128/8=16) or 32 bytes (for 256 bit -> 256/8=32) long (if shorter, padd it to 16/32)
- PW is padded = "filled up" with zeroes (what I see in your given example). Usually you hash it to 16/32 bytes (depends on the setting of MySQL)
- The message must be a multiple of 16 as AES is a block cypher (= block of 16), too

You can post an example string + password + the used iv here (test not the real ones). I will try to decrypt it then.
 
Upvote 0
Top