Python HMAC-SHA256 signature differs from PHP sign

2019-08-03 05:44发布

问题:

I searched and found a few questions that sort of addressed what I am trying to accomplish, but I can't seem to quite put my finger on this. I'm new to programming and am trying to replicate a bit of code that's available to me by the vendor in a handful of languages (PHP, Ruby, Perl, C#). I've taken the PHP code and tried my best to replicate it in Python and have gotten everything working except this signature function. Basically, I have to take an array of data that's being passed in by a user via web form, sign it, and send it to the web services listener to process. Try as I might, I can't get the signature values to match. The vendor unfortunately does not provide code-level support for anything other than their samples, so I'm out of luck there. Here's the PHP sample:

<?php

define ('HMAC_SHA256', 'sha256');
define ('SECRET_KEY', 'secret_key_redacted');

function sign ($params) {
  return signData(buildDataToSign($params), SECRET_KEY);
}

function signData($data, $secretKey) {
    return base64_encode(hash_hmac('sha256', $data, $secretKey, true));
}

function buildDataToSign($params) {
        $signedFieldNames = explode(",",$params["signed_field_names"]);
        foreach ($signedFieldNames as &$field) {
           $dataToSign[] = $field . "=" . $params[$field];
        }
        return commaSeparate($dataToSign);
}

function commaSeparate ($dataToSign) {
    return implode(",",$dataToSign);
}

?>

which is invoked as such:

<?php
foreach($params as $name => $value) {
    echo "<input type=\"text\" id=\"" . $name . "\" name=\"" . $name . "\" value=\"" . $value . "\"/>\n";
}

echo "<input type=\"text\" id=\"signature\" name=\"signature\" value=\"" . sign($params) . "\"/>\n";
?>

This is what I've used in Python to attempt to replicate:

def payment_confirmation(self, *args, **kw):
vars = cherrypy.request.params #takes POST parameters from previous page
del vars['submit'] #removed so it doesnt get signed - part of POST from prev. page

def sign_data(vars):
    for key, value in vars.iteritems():
        yield "%s=%s," % (key, value)

sign_payload = ''.join(sign_data(vars)).rstrip(',')
sign_signature = hashlib.sha256(sign_payload + secret_key).digest().encode('base64')

Here's a sample of the data I'm trying to sign (reduced into the object that's being returned via the sign_data() method in the Python code:

access_key=12345,reference_number=123456789,currency=USD,locale=en,profile_id=1234567,transaction_type=authorization,signed_date_time=2013-07-22T16:30:43Z,amount=25.00,transaction_uuid=0dc4a151-f2ec-11e2-bb29-005056c00008,payment_method=card,signed_field_names=access_key,profile_id,transaction_uuid,payment_token,signed_field_names,signed_date_time,locale,payment_method,transaction_type,amount,reference_number,currency,payment_token=0000000000000000

I realize it's a long-shot, but does anyone see anything that's obviously incorrect about my signature method in Python vs. the provided method in PHP? I thought it could be a way that the PHP function is encoding the key-value pairs so I tried messing around with that in the Python code but it doesn't seem to have worked. Thanks in advance!

回答1:

In case you're still stuck on that question, you might want to try using:

sign_signature = base64.b64encode(hmac.new(secret_key, sign_payload, hashlib.sha256).hexdigest())

The key here is to use hexdigest() instead of digest().

In my case I lost 2 hours finding out why Python did not produce the same result as PHP did when trying to sign Facebook API requests with their new "app secret proof".