Un-Encrypting/Re-Encrypting a Coldfusion Encrypted String in PHP

Un-encrypting / re-encrypting a ColdFusion encrypted string in PHP

Don't know how much help this will be but I have had the following working. I think to make CF happy you have to pad your encryption to a certain length

Encrypt in CF

Encrypt(data, encKey, 'AES/CBC/PKCS5Padding', encoding, encIv)

Decrypt in PHP

function Decode($data, $encKey, $encIv, $format = 'uu') {
if ($format === 'uu') {
$data = Convert_uudecode($data);
} else if ($format === 'hex') {
$data = Pack('H*', $data);
} else if ($format === 'base64') {
$data = Base64_Decode($data);
} else if ($format === 'url') {
$data = UrlDecode($data);
}
$data = MCrypt_decrypt(MCRYPT_RIJNDAEL_128, $encKey, $data, 'cbc', $encIv);
$pad = Ord($data{strlen($data)-1});
if ($pad > strlen($data)) return $data;
if (strspn($data, chr($pad), strlen($data) - $pad) != $pad) return $data;
return substr($data, 0, -1 * $pad);
}

Encrypt in PHP

function Encode($data, $encKey, $encIv, $format = 'uu') {
$pad = 16 - (StrLen($data) % 16);
if ($pad > 0) {
$data .= Str_repeat(Chr($pad), $pad);
}
$data = MCrypt_encrypt(MCRYPT_RIJNDAEL_128, $encKey, $data, 'cbc', $encIv);
if ($format === 'uu') {
return Convert_uuencode($data);
} else if ($format === 'hex') {
return Bin2Hex($data);
} else if ($format === 'base64') {
return Base64_Encode($data);
} else if ($format === 'url') {
return UrlEncode($data);
}
}

Decrypt in CF

Decrypt(data, encKey, 'AES/CBC/PKCS5Padding', encoding, encIv)

For some reason that I can't remember, I favoured 'uu' for the encoding.

Decrypting coldfusion encrypted string in PHP

I was able to use the following code to decrypt your example string.

<?php
require __DIR__ . '/vendor/autoload.php';

use AwkwardIdeas\PHPCFEncrypt\Encrypt;

$stringToDecrypt = '6A968A969DEB9A16549C61EE2EFE40A6515E';
$key = '2450RDSET0C';
$decrypted = Encrypt::decrypt($stringToDecrypt, $key, 'CFMX_COMPAT', 'hex');
var_dump($decrypted);
$ php main.php
string(18) "SofortUeberweisung"

After installing the dependency with composer.

composer require awkwardideas/phpcfencrypt

Here's a GitHub Gist you can use to test with.

https://gist.github.com/AlexanderOMara/b9bb6ff2a57bd0cf61fa8f0823d9a2a0

Just run composer install first.

NOTE: This encryption scheme is rather weak!

Hopefully you are using this decryption code as part of a migration process to move to a stronger encryption scheme, like AES (or a password hash like bcrypt if this is for passwords).

PHP encryption code converted to ColdFusion

A couple of thanks should go out before I answer my own question. Thanks to Dave Boyer (Mister Dai), Jason Dean and Jason Delmore for their help.

As Leigh has suggested I had to make use of Bouncy Castle, the light weight API and the Rijndael cipher engine there in.

I ended up with a function to create an rijndael cipher and functions to encrypt and decrypt a string with a key and ivsalt.

<cfcomponent displayname="Bounce Castle Encryption Component" hint="This provides bouncy castle encryption services" output="false">

<cffunction name="createRijndaelBlockCipher" access="private">
<cfargument name="key" type="string" required="true" >
<cfargument name="ivSalt" type="string" required="true" >
<cfargument name="bEncrypt" type="boolean" required="false" default="1">
<cfargument name="blocksize" type="numeric" required="false" default=256>
<cfscript>
// Create a block cipher for Rijndael
var cryptEngine = createObject("java", "org.bouncycastle.crypto.engines.RijndaelEngine").init(arguments.blocksize);

// Create a Block Cipher in CBC mode
var blockCipher = createObject("java", "org.bouncycastle.crypto.modes.CBCBlockCipher").init(cryptEngine);

// Create Padding - Zero Byte Padding is apparently PHP compatible.
var zbPadding = CreateObject('java', 'org.bouncycastle.crypto.paddings.ZeroBytePadding').init();

// Create a JCE Cipher from the Block Cipher
var cipher = createObject("java", "org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher").init(blockCipher,zbPadding);

// Create the key params for the cipher
var binkey = binarydecode(arguments.key,"hex");
var keyParams = createObject("java", "org.bouncycastle.crypto.params.KeyParameter").init(BinKey);

var binIVSalt = Binarydecode(ivSalt,"hex");
var ivParams = createObject("java", "org.bouncycastle.crypto.params.ParametersWithIV").init(keyParams, binIVSalt);

cipher.init(javaCast("boolean",arguments.bEncrypt),ivParams);

return cipher;
</cfscript>
</cffunction>

<cffunction name="doEncrypt" access="public" returntype="string">
<cfargument name="message" type="string" required="true">
<cfargument name="key" type="string" required="true">
<cfargument name="ivSalt" type="string" required="true">

<cfscript>
var cipher = createRijndaelBlockCipher(key=arguments.key,ivSalt=arguments.ivSalt);
var byteMessage = arguments.message.getBytes();
var outArray = getByteArray(cipher.getOutputSize(arrayLen(byteMessage)));
var bufferLength = cipher.processBytes(byteMessage, 0, arrayLen(byteMessage), outArray, 0);
var cipherText = cipher.doFinal(outArray,bufferLength);

return toBase64(outArray);
</cfscript>
</cffunction>

<cffunction name="doDecrypt" access="public" returntype="string">
<cfargument name="message" type="string" required="true">
<cfargument name="key" type="string" required="true">
<cfargument name="ivSalt" type="string" required="true">

<cfscript>
var cipher = createRijndaelBlockCipher(key=arguments.key,ivSalt=arguments.ivSalt,bEncrypt=false);
var byteMessage = toBinary(arguments.message);
var outArray = getByteArray(cipher.getOutputSize(arrayLen(byteMessage)));
var bufferLength = cipher.processBytes(byteMessage, 0, arrayLen(byteMessage), outArray, 0);
var originalText = cipher.doFinal(outArray,bufferLength);

return createObject("java", "java.lang.String").init(outArray);
</cfscript>
</cffunction>

<cfscript>
function getByteArray(someLength)
{
byteClass = createObject("java", "java.lang.Byte").TYPE;
return createObject("java","java.lang.reflect.Array").newInstance(byteClass, someLength);
}
</cfscript>

</cfcomponent>

The doEncrypt and doDecrypt functions are publically visible, but not the function that creates the rijndael cipher. The encryption and decryption functions take a string, key and ivSalt returning an encrypted or decrypted string respectively.

The createRijndaelBlockCipher takes a key, ivSalt, a boolean to state whether the cipher will be used to encrypt or decrypt and the block size, although the block size is defaulted to 256 bits. The function is fairly well commented so it should make sense.

The UDF at the bottom (special thanks to Jason Delmore for that nugget) ensures that ColdFusion correctly creates a byte array for the decryption. Some other ways of creating byte arrays just don't work or end up with inconsistent results in decryption or throw pad buffer corrupt errors.

That's about it really. It took far too much effort, when the standard AES encryption uses 128bit blocks and 128 Bit Keys are for classified up to SECRET, 192-bit or higher for TOP-SECRET. 256bit blocks and 256bit keys are just a bit over the top. Just because you can doesn't mean you should.

Please do remember that MCRYPT_RIJNDAEL_256 is the block size and not the encryption level. The encryption level is set by the strength of key that you pass into mcrypt_encrypt and increasing the block size does not increase the encryption strength.

PHP mcrypt to ColdFusion decrypt

Something like this should work. You just need to share a common key between each.

In PHP:

base64_encode(mcrypt_encrypt(MCRYPT_3DES, $key, $plain_string, MCRYPT_MODE_ECB));

In Coldfusion:

<cfscript>
decrypted_string = decrypt(enc_string, key, "DESEDE", "Base64");
</cfscript>

Decrypt AES from Coldfusion to PHP

$key = base64_decode($key);

$data = base64_decode($data);

echo mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_ECB);

ColdFusion String to Hex to SHA-2 - Not Encrypting Properly

If I do this in PHP:

echo hash("sha256", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor");

I get

1c3f958abd85c54905c97fe8e0628fe76495711962a27daae34033781486da00

and this in CF:

hash("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor", "SHA-256" )

I get

1C3F958ABD85C54905C97FE8E0628FE76495711962A27DAAE34033781486DA00 

Spot the difference? Is your payment provider doing a case-sensitive check? If not, then it must be a problem with how you're building up your string that you end up hashing.

The PHP example takes a string, loops over it, gets the ASCII decimal value of each character, converts it to hexadecimal.

Your CF version takes a string, converts the whole thing to binary, then to hexadecimal. Why not just use the same method? When I do this in CF I get the same values of the string that ends up being hashed:

for (var i = 1; i <= len(str); i++) {
hex_str &= formatBaseN(asc(mid(str, i, 1)), 16);
}

Coldfusion Decrypt with special characters

I will get an error. I know that in this example i should switch # to
##. But what should i do with other special characters in my database ?

Nothing. You can store whatever characters you want. You will not get that error when you invoke decrypt() with your query values.

"Invalid CFML construct found.." is a compilation error. It occurs before any CF code is even executed. The only reason it occurs in your example is because the # is embedded within the CF code itself. So when the CF server parses and compiles that code, it see the un-escaped # sign as the start of some sort of variable and looks for a closing # sign. When it does not find one where it expects, it causes an error and the compilation fails. So unescaped # signs are only an issue when they are contained within that actual CF code (or a string evaluated as CF code).

When you retrieve the encrypted text from the db table, CF does not evaluate the query values as code. It just pulls the strings from the db and hands them off to the decrypt function. So that error cannot occur.

Having said all that, you really should not use CFMX_COMPAT - for anything. It is not really encryption at all, but rather a legacy obfuscation algorithm maintained for backward compatibility only. Instead use a real encryption algorithm like AES, Blowfish, etcetera. You might also want to use "base64" or "hex" instead of "UU", as they former are more portable. See the encrypt() docs for a list of the supported algorithms.

ColdFusion Decrypt AES128 PKCS7Padding CBC

I got it working!

  • The dataString needed to be in base64 instead of hex.

  • The key, when converted to hex using coldFusion is 24 [0-9A-F] chars representing 24x4=96 bits which is 12 Bytes. It needs to be 16 Bytes, so somehow the BinaryEncode(ToBinary()) function doesn't do its job. So I used a string to hex tool to convert it and then subsequently got 32 chars.
    Edit 2: Also, to install Bouncy Castle I followed the bouncy castle installe instructions and downloaded jce from the bottom of the java downloads page.



Related Topics



Leave a reply



Submit