Python3 and hmac . How to handle string not being binary

PythonStringPython 3.xEncodingHmac

Python Problem Overview


I had a script in Python2 that was working great.

def _generate_signature(data):
   return hmac.new('key', data, hashlib.sha256).hexdigest()

Where data was the output of json.dumps.

Now, if I try to run the same kind of code in Python 3, I get the following:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/hmac.py", line 144, in new
    return HMAC(key, msg, digestmod)
  File "/usr/lib/python3.4/hmac.py", line 42, in __init__
    raise TypeError("key: expected bytes or bytearray, but got %r" %type(key).__name__)
TypeError: key: expected bytes or bytearray, but got 'str'

If I try something like transforming the key to bytes like so:

bytes('key')

I get

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: string argument without an encoding

I'm still struggling to understand the encodings in Python 3.

Python Solutions


Solution 1 - Python

You can use bytes literal: b'key'

def _generate_signature(data):
    return hmac.new(b'key', data, hashlib.sha256).hexdigest()

In addition to that, make sure data is also bytes. For example, if it is read from file, you need to use binary mode (rb) when opening the file.

Solution 2 - Python

Not to resurrect an old question but I did want to add something I feel is missing from this answer, to which I had trouble finding an appropriate explanation/example of anywhere else:

Aquiles Carattino was pretty close with his attempt at converting the string to bytes, but was missing the second argument, the encoding of the string to be converted to bytes.

If someone would like to convert a string to bytes through some other means than static assignment (such as reading from a config file or a DB), the following should work:

(Python 3+ only, not compatible with Python 2)

import hmac, hashlib

def _generate_signature(data):
  key = 'key' # Defined as a simple string.
  key_bytes= bytes(key , 'latin-1') # Commonly 'latin-1' or 'ascii'
  data_bytes = bytes(data, 'latin-1') # Assumes `data` is also an ascii string.
  return hmac.new(key_bytes, data_bytes , hashlib.sha256).hexdigest()

print(
  _generate_signature('this is my string of data')
)

Solution 3 - Python

try

> codecs.encode()

which can be used both in python2.7.12 and 3.5.2

import hashlib
import codecs
import hmac

a = "aaaaaaa"
b = "bbbbbbb"
hmac.new(codecs.encode(a), msg=codecs.encode(b), digestmod=hashlib.sha256).hexdigest()

enter image description here enter image description here

Solution 4 - Python

for python3 this is how i solved it.

import codecs
import hmac

def _generate_signature(data):
  return hmac.new(codecs.encode(key), codecs.encode(data), codecs.encode(hashlib.sha256)).hexdigest()
    

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAquiles CarattinoView Question on Stackoverflow
Solution 1 - PythonfalsetruView Answer on Stackoverflow
Solution 2 - PythonJoshua BurnsView Answer on Stackoverflow
Solution 3 - PythontinyhareView Answer on Stackoverflow
Solution 4 - PythonvinchuliView Answer on Stackoverflow