.netencryptioncoldfusionrailo3des

DESede (3DES) Encryption in RAILO CFML


I've been trying for many days to try and solve this issue.

A 3rd Party webservice requires me to send encrypted data using 3DES with ECB and PKCS7padding - They're using .NET

The encrypt function within railo CFML is limited to DESede without any further options (i.e. defaults are used for cipher mode and padding).

Has anyone had this issue in Railo and come up with a solution (java based perhaps)? - I'm seriously pulling my hair out here!

Based on Leigh's suggestions below I made some changes:

I've made a bit of progress, I think this key has some kind of MD5 hashing on it.

I fished around the web and modified your solution a bit - I don't think the padding is necessary but the resultant encrypted string at first appears to be what I should expect but then closer on inspection it's incorrect:

IvParameterSpec = createObject("java", "javax.crypto.spec.IvParameterSpec");
Cipher = createObject("java", "javax.crypto.Cipher");
SecretKeySpec = createObject("java", "javax.crypto.spec.SecretKeySpec");
BASE64Decoder = createObject("java", "sun.misc.BASE64Decoder");
Str = createObject("java", "java.lang.String");
MessageDigest = createObject("java", "java.security.MessageDigest");

input  = "<xml><PanNumber>6280390027626871</PanNumber><Req_Currency_Code>826</Req_Currency_Code><Card_Pin>1234</Card_Pin><Till_Amount></Till_Amount><Auth_Code></Auth_Code></xml>";
key = "06098140901984F95E139F29B479D952CB6545C177D21456";

md = MessageDigest.getInstance("MD5");
md.update(key.getBytes("UTF-8"), 0, key.length());
keyBytes = md.digest();
newKey = tobase64(keyBytes);
keyBytes2  = binaryDecode(newKey, "base64");
keyBytes2  = arrayMerge(keyBytes, arraySlice(keyBytes, 1, 8));
allnewKey = binaryEncode(javacast("byte[]", keyBytes2), "base64");

encrypted = encrypt(input, allnewKey, "desede", "base64");
WriteDump("encrypted (CF): "& encrypted);`

The result is: 26sfwv2DHDj7EHYd5Qao8veDtPbKIcv8rDVhbLPDEaWHO27EUGRF6KrdbXe7NBUVADYMdGuagfO4Tev584dUcgKGJ2h6kWPZxooNUGMgL2xB7e00YOkLosA8wFD569sZUd1MGKuF9yCjY1zCsAE4SgohkcuK9YZ7BizQma99/W9yOsIjAfHtAqGiep4tMTQ+eFASYtPybccsgi8H4brIB/HAu0kaDSAw

The expected result is: 26sfwv2DHDj7EHYd5Qao8veDtPbKIcv8rDVhbLPDEaWHO27EUGRF6MxaAzUpJDqQBq8NGgdqmtn6q/wVQNHGWrOE8+aetKVC78nszS3ZO8AHjwoT1igv4lGl78n8jCHHU+KwnBT7KfXIYMTCuwO/MohIiFbGyhMXPsvv3/G4OY1C2nEkN0LweLh4mTgtU8syT1M9XdmvwhaltsmPoFtoE9FujvQpJCY3


Solution

  • The encrypt function within railo CFML is limited to DESede without any further options (i.e. defaults are used for cipher mode and padding).

    Yes, I believe it uses java's defaults ie DESede/ECB/PKCS5Padding which are compatible with TripleDES/ECB/PKCS7padding in .NET. So it should work right out of the box as long as you are using a 24 byte key.

    Without knowing more, I am guessing it might be a problem with your key size. .NET supports both 16 and 24 byte keys, but java only supports 24 byte keys. So if your key is only 16 bytes, you need to pad it with the first eight (8) bytes to make it acceptable to Java/Railo.

    CF/Railo Code

    <cfscript>
        input  = "DESede (3DES) Encryption in RAILO CFML";
        key    = "ru8femXhTm9jwdGdhb/4Sw==";
    
        // pad the key with the first eight bytes. then convert back to base64
        keyBytes  = binaryDecode(key, "base64");
        keyBytes  = arrayMerge(keyBytes, arraySlice(keyBytes, 1, 8));
        newKey = binaryEncode(javacast("byte[]", keyBytes), "base64");
    
        encrypted = encrypt(input, newKey, "desede", "base64");
        WriteDump("encrypted (CF): "& encrypted);
    </cfscript>
    

    C# Code

    byte[] input = Encoding.UTF8.GetBytes("DESede (3DES) Encryption in RAILO CFML");
    byte[] key = Convert.FromBase64String("ru8femXhTm9jwdGdhb/4Sw==");
    
    TripleDESCryptoServiceProvider algorithm = new TripleDESCryptoServiceProvider();
    algorithm.Mode = CipherMode.ECB;
    algorithm.BlockSize = 64;
    algorithm.KeySize = 128; // 16 byte key
    algorithm.Key = key;
    ICryptoTransform cipher = algorithm.CreateEncryptor();
    byte[] encrypted = cipher.TransformFinalBlock(input, 0, input.Length);
    Console.WriteLine("encrypted (.NET): {0}", Convert.ToBase64String(encrypted));
    

    Results:

    encrypted (CF):   fMPlk0ZqHDwp2zzZs/Cng7Y6r8Acr55UPJYWJTruEesxkBApsEFo6w==
    encrypted (.NET): fMPlk0ZqHDwp2zzZs/Cng7Y6r8Acr55UPJYWJTruEesxkBApsEFo6w==
    

    Update: Weird. When I MD5 hash the key in .NET I get your first result rather than the "expected result"

        String rawInput = "<xml><PanNumber>6280390027626871</PanNumber><Req_Currency_Code>826</Req_Currency_Code><Card_Pin>1234</Card_Pin><Till_Amount></Till_Amount><Auth_Code></Auth_Code></xml>";
        String rawKey = "06098140901984F95E139F29B479D952CB6545C177D21456";
        byte[] input = Encoding.UTF8.GetBytes(rawInput);
        byte[] key = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(rawKey));
        // ... rest of code 
    

    Result:

    encrypted (.NET): 26sfwv2DHDj7EHYd5Qao8veDtPbKIcv8rDVhbLPDEaWHO27EUGRF6KrdbXe7NB
    UVADYMdGuagfO4Tev584dUcgKGJ2h6kWPZxooNUGMgL2xB7e00YOkLosA8wFD569sZUd1MGKuF9yCjY1
    zCsAE4SgohkcuK9YZ7BizQma99/W9yOsIjAfHtAqGiep4tMTQ+eFASYtPybccsgi8H4brIB/HAu0kaDS
    Aw