2

AES Encryption in PHP and ColdFusion9 is producing different results. Could somebody please help me?

The below PHP Code

$key = "12345678123456781234567812345678";
$iv = "1234567812345678";
$data = "This is a plain string.";

echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv));

gives me G+tdEOfQTtVCQGxW3N5uzkqN207OyfIPxS6zf2xrKKY=

While the below ColdFusion Code

<cfset thePlainData  = "This is a plain string." />
<cfset theKey    = "12345678123456781234567812345678" />
<cfset theAlgorithm  = "AES/CBC/PKCS5Padding" />
<cfset theEncoding  = "base64" />
<cfset theIV    = "1234567812345678" />

<cfset encryptedString = encrypt(thePlainData, theKey, theAlgorithm, theEncoding, theIV) />

gives me KLt55n5/T3ee6xVq9VGFbyCacJznkHEqC/RDRhL+4nw=

Any idea where I am wrong? Thanks in advance.

user812120
  • 585
  • 1
  • 10
  • 21
  • The PHP above runs for me, but the CF above throws an error: "The key specified is not a valid key for this encryption: Illegal key size"...is that *truly* the exact CF code? Is this CF9 Enterprise? – Shawn Holmes Apr 24 '12 at 16:02
  • Many thanks for your quick response. I am using ColdFusion9 Trial Version. – user812120 Apr 24 '12 at 16:03
  • CF expects `theKey` to be in base64. Converting `theKey` to base64 gets you closer, but not all the way there. So it could be an encoding difference. Can you print out both `theKey/iv` in either hex or base64? – Leigh Apr 24 '12 at 19:51

2 Answers2

6

Unfortunately there is a slight incompatibility between the ColdFusion and PHP implementations regarding the plaintext padding style used. AES requires a plaintext block size divisible by 128. To achieve this, PHP will pad the plaintext input with NULL characters to get the proper block size. ColdFusion can use a variety of padding techniques that are supported by Java. Unfortunately, ColdFusion nor Java support a NULL padding schema which makes interoperability more difficult. ColdFusion's string handling does not support NULL characters, so you will need to implement a PKCS5Padding schema within PHP instead to get them to inter-operate properly.

Also, as mentioned in the comments, ColdFusion will expect the key to be base64 encoded, so you'd need the key setting to look like:

<cfset theKey = toBase64("12345678123456781234567812345678") />

Further, Java by default (and ColdFusion by extension) only supports key sizes up to 128 bits. Here you're using a 256 bit key which would require the Java Unlimited Strength extension (for those trying to test the code and getting an illegal key size error).

The resulting PHP code looks like:

// Function from http://us3.php.net/manual/en/ref.mcrypt.php#69782
function pkcs5_pad ($text, $blocksize)
{
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

$key = "12345678123456781234567812345678";
$iv = "1234567812345678";
// Pad data with PKCS #5 to prevent PHP from using NULL padding.
$data = pkcs5_pad("This is a plain string.", 16);

echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv));

The resulting ColdFusion code looks like:

<cfset thePlainData = "This is a plain string." />
<cfset theKey = toBase64("12345678123456781234567812345678") />
<cfset theAlgorithm = "AES/CBC/PKCS5Padding" />
<cfset theEncoding = "base64" />
<cfset theIV = "1234567812345678" />

<cfset encryptedString = encrypt(thePlainData, theKey, theAlgorithm, theEncoding, theIV) />

<cfoutput>#encryptedString#</cfoutput>

Both output the same base64 encoded string:

G+tdEOfQTtVCQGxW3N5uzlu0mGabRKNxuIdAXArQE80=
Justin Scott
  • 865
  • 4
  • 10
  • Thank you very much Justin for your detailed answer. It solved the issue. – user812120 Apr 25 '12 at 08:31
  • Is it possible to encrypt data in PHP using MCRYPT_RIJNDAEL_256 instead of MCRYPT_RIJNDAEL_128 and then decrypt it in ColdFusion or Ruby using AES Decryption. – user812120 Apr 25 '12 at 08:49
  • Not out of the box, no. AES is based on Rijndael but is more restrictive about the block size and key lengths. AES only supports a plaintext block size of 128 bits, so if you're using Rijndael and AES together, you have to use the 128 bit block size on the Rijndael side for them to be compatible. (Recall that the "128" in "MCRYPT_RIJNDAEL_128" specifies the block size, not the key length.) – Justin Scott Apr 25 '12 at 14:10
  • Having said that, ColdFusion runs atop and has access to Java, so you can use a Java-based Rijndael library instead to get more flexibility. This StackOverflow answer (http://stackoverflow.com/questions/5808107/php-encryption-code-converted-to-coldfusion) talks about how to use the Bouncy Castle library for Java and use it within ColdFusion to get greater flexibility and compatibility with other Rijndael ciphers. – Justin Scott Apr 25 '12 at 14:16
  • Ah, so it is was the padding scheme. Nice explanation Justin! +1 – Leigh Apr 25 '12 at 16:17
  • Any ideas about this http://stackoverflow.com/questions/10331980/coldfusion-equivalent-to-php-hash-hmac/10333126#10333126 – user812120 Apr 26 '12 at 15:16
0

I know this is an old thread, but a similar question came up recently.

While not supported natively, it turns out there is a way to produce null padding in CF. This answer by Artjom B. agrees it may be simpler to adjust the padding in PHP, but points out you could achieve the same result padding the plain text with 0x00, to a multiple of the algorithm's block size and using the "NoPadding" scheme.

Producing null characters in CF is a little tricky, but can be done using URLDecode("%00"). Since encrypt() always treats input as UTF-8 encoded, you can also use charsetEncode() to create a null character from a single element byte array, ie charsetEncode( javacast("byte[]", [0] ), "utf-8").

Not highly tested, but something like this should produce the same result in CF10:

Code:

thePlainData = nullPad("This is a plain string.", 16);
// NB: JCE unlimited policy files required for 256 bit keys
theKey = toBase64("12345678123456781234567812345678");
theIV  = "1234567812345678";

encryptedString = encrypt(thePlainData, theKey, "AES/CBC/NoPadding", "base64", theIV);

Result:

G+tdEOfQTtVCQGxW3N5uzkqN207OyfIPxS6zf2xrKKY=

Function:

/*
   Pads a string, with null bytes, to a multiple of the given block size

   @param plainText - string to pad
   @param blockSize - pad string so it is a multiple of this size
   @param encoding - charset encoding of text
*/
string function nullPad( string plainText, numeric blockSize, string encoding="UTF-8")
{
    local.newText = arguments.plainText;
    local.bytes = charsetDecode(arguments.plainText, arguments.encoding);
    local.remain = arrayLen( local.bytes ) % arguments.blockSize;

    if (local.remain neq 0) 
    {
        local.padSize = arguments.blockSize - local.remain;
        local.newText &= repeatString( urlDecode("%00"), local.padSize );
    }

    return local.newText;
}
Community
  • 1
  • 1
Leigh
  • 28,765
  • 10
  • 55
  • 103