Search code examples
phpcoldfusionhmacsha1

ColdFusion binaryDecode vs php pack(H*)


I need to create in ColdFusion a 20-characters usable key from a 40-characters key. It's to calculate a HMAC signature.

The server I'm calling verifies the signature with the php package (H *) function.

When I try to create the key to compute the HMAC seal, I cannot calculate the same key as returned by PHP. Pack (H *) returns well 20 characters, but CF (binaryDecode ()), returns 18, some are ignored or the result is different. The signature is not valid.

Ex : If I try to calculate a 20-characters key with this one

"325A16A325127FD42B700D4810E83F6312877B92":

PHP return : 2Z�%�+p H�?c�{�, and with CF : 2Z�%�+pH�?c�{�

$key = "325A16A325127FD42B700D4810E83F6312877B92";
$test = pack('H*',$key);
var_dump($test);

With CF :

local.key   = toString(binaryDecode("325A16A325127FD42B700D4810E83F6312877B92", "hex"));
writeDump(local.key);

More code :

PHP.

function hmac_sha1 ($key, $data) {

    $length = 64; // block length for SHA1
    if (strlen($key) > $length) { $key = pack("H*",sha1($key)); }
    $key  = str_pad($key, $length, chr(0x00));
    $ipad = str_pad('', $length, chr(0x36));
    $opad = str_pad('', $length, chr(0x5c));
    $k_ipad = $key ^ $ipad ;
    $k_opad = $key ^ $opad;

    return sha1($k_opad  . pack("H*",sha1($k_ipad . $data)));


}
$key = "325A16A325127FD42B700D4810E83F6312877B92";
$validKey = pack('H*',$key);
$str = "7464052*08/10/2018:14:22:30*65.25EUR*AA123**3.0*FR*carmen*[email protected]**********";
$sign = hmac_sha1($validKey, $str);
$test = pack('H*',$key);

Return: 23e7db20da9b58a47e27c151a65c2393a08ee4f5

local.key = toString(binaryDecode("325A16A325127FD42B700D4810E83F6312877B92","hex"));
local.crypto = createObject("miscellaneous.crypto").init();
local.str = "7464052*08/10/2018:14:22:30*65.25EUR*AA123**3.0*FR*carmen*[email protected]**********";
local.sign = local.crypto.hmacSha1(local.key, local.str, "hex");

Return: ff8d510f348d1a9b3652b33b8e7780c9f8d4536e


Solution

  • The problem is combination of how you're passing in the hexadecimal key and how that custom component uses it in creating the signature. The code inside _getMacInstance(), which creates the secretKeySpec, manipulates the supplied value - and in this case - produces a totally different key. That's why the signatures don't match:

        var secretkeySpec = createObject( "java", "javax.crypto.spec.SecretKeySpec" ).init(
            toBinary( toBase64( key ) ), // Changes the key value
            javaCast( "string", algorithm )
        );
    

    Technically you could make it work by getting rid of all the conversions and just decoding the key from hex instead:

        // Create the specification for our secret key.
        var secretkeySpec = createObject( "java", "javax.crypto.spec.SecretKeySpec" ).init(
            binaryDecode(arguments.key, "hex"), // Decodes raw HEX key
            javaCast( "string", algorithm )
        );
    

    Then passing in a raw hex key

    crypto.hmacSha1("325A16A325127FD42B700D4810E83F6312877B92",value, "hex");
    

    Produces the same result as PHP 23e7db20da9b58a47e27c151a65c2393a08ee4f5

    However, you may want to modify how the component handles keys instead. So it's a little less brittle. For example, you could modify the hmac* functions to accept binary or perhaps even support different encodings by using arguments.encoding. All depends on how you intend to use the CFC.