Steve
Steve

Reputation: 3095

CF8 and AES decrypting MySQL AES: encodings are not same

This has become more of an exercise in what am I doing wrong than mission critical, but I'd still like to see what (simple probably) mistake I'm making.

I'm using mysql (5.1.x) AES_ENCRYPT to encrypt a string. I'm using CF's generateSecretKey('AES') to make a key (I've tried it at defaul and 128 and 256 bit lengths).

So let's say my code looks like this:

    <cfset key = 'qLHVTZL9zF81kiTnNnK0Vg=='/>
    <cfset strToEncrypt = '4111111111111111'/>
    <cfquery name="i" datasource="#dsn#">
        INSERT INTO table(str) 
            VALUES AES_ENCRYPT(strToEncrypt,'#key#');
    </cfquery>

That works fine as expected and I can select it using SELECT AES_DECRYPT(str,'#key#') AS... with no problems at all.

What I can't seem to do though is get CF to decrypt it using something like:

    <cfquery name="s" datasource="#dsn#">
        SELECT str
          FROM table
    </cfquery>
    <cfoutput>#Decrypt(s.str,key,'AES')#</cfoutput>

or

    <cfoutput>#Decrypt(toString(s.str),key,'AES')#</cfoutput>

I keep getting "The input and output encodings are not same" (including the toString() - without that I get a binary data error). The field type for the encrypted string in the db is blob.

Upvotes: 4

Views: 947

Answers (1)

Leigh
Leigh

Reputation: 28873

This entry explains that mySQL handles AES-128 keys a bit differently than you might expect:

.. the MySQL algorithm just or’s the bytes of a given passphrase against the previous bytes if the password is longer than 16 chars and just leaves them 0 when the password is shorter than 16 chars.

Not highly tested, but this seems to yield the same results (in hex).

<cfscript>
    function getMySQLAES128Key( key ) {
        var keyBytes   = charsetDecode( arguments.key, "utf-8" );
        var finalBytes = listToArray( repeatString("0,", 16) );

        for (var i = 1; i <= arrayLen(keyBytes); i++) {
            // adjust for base 0 vs 1 index
            var pos = ((i-1) % 16) + 1;
            finalBytes[ pos ] = bitXOR(finalBytes[ pos ], keyBytes[ i ]);
        }

        return binaryEncode( javacast("byte[]", finalBytes ), "base64" );
    }

    key     = "qLHVTZL9zF81kiTnNnK0Vg==";
    input   = "4111111111111111";

    encrypted = encrypt(input, getMySQLAES128Key(key), "AES", "hex");
    WriteDump("encrypted="& encrypted);

    // note: assumes input is in "hex". either convert the bytes 
    // to hex in mySQL first or use binaryEncode
    decrypted = decrypt(encrypted, getMySQLAES128Key(key), "AES", "hex");
    WriteDump("decrypted="& decrypted);
</cfscript>

Note: If you are using mySQL for encryption be sure to see its documentation which mentions the plain text may end up in various logs (replication, history, etectera) and "may be read by anyone having read access to that information".


Update: Things may have changed, but according to this 2004 bug report the .mysql_history file is only on Unix. (Keep in mind there may be other log files) Detailed instructions for clearing .mysql_history can be found in the manual, but in summary:

  • Set the MYSQL_HISTFILE variable to /dev/null (on each log in)
  • Create .mysql_history as a symbolic link to /dev/null (only once)

Upvotes: 5

Related Questions