Reputation: 83735
I am having a slight problem with HMAC. When running this piece of code:
signature = hmac.new(
key=secret_key,
msg=string_to_sign,
digestmod=sha1,
)
I get a strange error:
File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hmac.py", line 133, in new
return HMAC(key, msg, digestmod)
File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/hmac.py", line 72, in __init__
self.outer.update(key.translate(trans_5C))
TypeError: character mapping must return integer, None or unicode
When I print string_to_sign, it is a proper string like this:
GET
\n
\n
application/json
\n
\n
\n
What does the error mean? Is it because of new lines?
Upvotes: 22
Views: 17427
Reputation: 43
In python 2
If you code it as:
from __future__ import unicode_literals
import base64
import hashlib
import hmac
import time
def gen_signature(key, strsome, expires=0):
expires = int(time.time()) + 600 if expires == 0 else expires
_2_signature = '%s\n%s\n' % (expires, strsome)
# hmac.new(key, msg, digestmod) bytearray(secret, 'utf-8')
signature = base64.b64encode(hmac.new(key, _2_signature, hashlib.sha1).digest())
return signature
gen_signature('xxxxxxx', 'strsome')
you get an error like the one you provided.
However, if you use bytearray(key, 'utf-8')
in place of the raw key variable and bytearray(_2_signature, 'utf-8')
in place place of the _2_signature variable, it should work properly.
For example:
def gen_signature(key, strsome, expires=0):
expires = int(time.time()) + 600 if expires == 0 else expires
_2_signature = '%s\n%s\n' % (expires, strsome)
# hmac.new(key, msg, digestmod) bytearray(secret, 'utf-8')
signature = base64.b64encode(hmac.new(bytearray(key, 'utf-8'), bytearray(_2_signature, 'utf-8'), hashlib.sha1).digest())
return signature
gen_signature('xxxxxxx', 'strsome')
Upvotes: 1
Reputation: 2963
Since I didn't want to write in our API docs that you should convert the payload to ASCII or drop Unicode characters before comparing digests, I've used the following solution:
import hmac
import hashlib
def sign_request(self, secret, data):
return hmac.new(
key=bytearray(secret, 'utf-8'),
msg=bytearray(data, 'utf-8'),
digestmod=hashlib.sha256
).hexdigest()
bytearray
turns my unicode string into bytes using the utf-8
encoding.key
and msg
are bytes parameters (like hmac
library is expecting).Upvotes: 1
Reputation: 161
Ensure that "key" and "msg" is a string.such as:
s = hmac.new(str(secretkey), str(message), digestmod=hashlib.sha1).hexdigest()
Upvotes: 16
Reputation: 4295
As asked I'll post this as an answer. The error that you faced is a feature of Python's HMAC. It does not accept unicode. This feature is described here.
HMAC is a function which works at byte level. For this reason in Python 3 it accepts only bytes
. In Python 2 we don't have bytes
so it accepts only str
.
Upvotes: 45