The important thing to understand the code example is that it refers to bytes. Your CF code uses characters. This may seem like a trivial difference, but these are completely different things that will give very different results. For successful decryption, you need to work with bytes (or binary) of these strings, and not with characters.
Although you can control binary arrays using basic CF functions such as arraySlice () , the syntax becomes a little cumbersome / awkward at times. The reason is because binary arrays are a different type of object than your standard CF array, i.e. Byte [] compared to java.util.List . Therefore, depending on what functions are used, you may need javacast to force the variables to the expected type. With that in mind.
Part I - Decrypt encKey
- Base64-decode encKey.
- Delete the first 32 bytes of the decoded value. This is HMAC (Hash Message Authentication Code). Calculate the HMAC SHA-256 for the rest of the decoded data using your common API and compare it to the HMAC from the first 32 bytes.
First, convert the base64 string to binary using binaryDecode . Then extract the appropriate number of bytes from the returned array. This is the expected HMAC value:
hmacSize = 32; binaryToDecrypt = binaryDecode(encryptedKey, "base64"); expectedHMAC = binaryEncode( javacast("byte[]", arraySlice(binaryToDecrypt, 1, hmacSize)) , "hex" );
Then extract all the remaining bytes and use them to calculate the actual HMAC. Check it for the expected value. If they do not match, something went wrong.
remainData = arraySlice(binaryToDecrypt, hmacSize + 1); actualHMAC = hmac( javacast("byte[]", remainData ), sharedSecret, "HMACSHA256"); if (compare(actualHMAC, expectedHMAC) != 0) { throw("ERROR: Invalid HMAC ["& actualHMAC &"]. Expected ["& expectedHMAC &"]"); }
- The next 16 bytes should be deleted and used as IV (Initialization Vector) for the decryption algorithm.
The remaining bytes contain an IV followed by an encrypted value. Before you can decrypt the latter, you need to extract and separate two:
ivSize = 16; ivValue = javacast("byte[]", arraySlice(remainData, 1, ivSize)); encryptedValue = javacast("byte[]", arraySlice(remainData, ivSize + 1));
- Decrypt the remaining data using AES-256-CBC, IV from step 3 and the SHA-256 hash of your secret API.
The final step before you can decrypt is to generate a decryption key by hashing the shared secret. Unfortunately, the CF hash () function always returns a hexadecimal string. Therefore, it must be converted to base64 format in order to be compatible with the decryption function.
keyHex = hash(sharedSecret, "SHA-256", "utf-8"); keyBase64 = binaryEncode(binaryDecode(keyHex, "hex"), "base64");
Finally, use all three values to decrypt. The returned binary will contain the encryption key used in Part II.
decryptedKeyBinary = decryptBinary( encryptedValue , keyBase64 , "AES/CBC/PKCS5Padding" , ivValue);
Part II - Decrypt data encPaymentData h2>
Use the same process as in the first part, just replace the variables:
- Use
encPaymentData instead of encryptedKey - Use
decryptedKeyBinary instead of sharedSecret .
The final, decrypted result will be binary. Use charsetEncode to convert it back to a human-readable string:
result = charsetEncode(decryptedResult, "utf-8");
NB: The sample values chosen were apparently broken because they do not even work with the java example. The above steps lead to the correct result when using valid values (key, data, etc.).