Python3和hmac。如何处理非二进制字符串?

54

我有一个在Python2中运行良好的脚本。

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

数据是由json.dumps生成的。

现在,如果我尝试在Python 3中运行同样的代码,我会得到以下结果:

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')

我明白了

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

我仍然在努力理解Python 3中的编码。


我得到了这个。TypeError:Unicode对象必须在哈希之前进行编码 - Trect
4个回答

52

您可以使用字节字符串: b'key'

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

除此之外,还要确保data也是字节类型的。例如,如果它是从文件中读取的,则需要在打开文件时使用二进制模式(rb)。


4
我该如何使用字节字面值作为参数? - Maxim Dunavicher
4
我仍然遇到了这样的错误:TypeError: Unicode-objects must be encoded before hashing。这意味着在对Unicode字符串进行哈希之前需要进行编码。 - Trect
@Tessaracter,你使用的是Python 2.x吗?如果是,那么需要编码数据,hmac.new(b'key', data.encode('utf-8'), hashlib.sha256).hexdigest()(根据需要更改 utf-8 编码)。 - falsetru
@falsetru。不是的,我使用Python 3.6。 - Trect
@falsetru。我被禁止提问了。不太确定该怎么办。(在元社区中读到我需要等待6个月) - Trect
显示剩余3条评论

42

不是为了重提一个旧问题,但我想补充一些我觉得在这个答案中缺失的东西,我在其他地方很难找到适当的解释/示例:

Aquiles Carattino 试图将字符串转换为字节时很接近,但缺少了第二个参数——要转换为字节的字符串的编码。

如果有人想通过除静态分配之外的其他方式(例如从配置文件或数据库读取)将字符串转换为字节,则应使用以下方法:

(仅适用于 Python 3+,不兼容 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')
)

1
请注意,如果代码位于Python 2.7包中但从Python 3调用,则此解决方案将无法正常工作。这是因为在Python 2中,“bytes”内置仅是“str”的别名。通过强制将字符串转换为字节数组,我能够编写一个函数,它可以在Python 2或3中运行,并且两个版本的语言都有效。 - mcbex
^ 确实如此,仅限于Python3。 - Joshua Burns
答案很好,但是有许多不必要的细节,不够精确和准确:对于拉丁字符,bytes(key, 'latin-1') === bytes(key, 'ascii') === key.encode('ascii'),但是对于Unicode支持,我们需要bytes(key, 'utf-8') === key.encode('utf-8')。 - F.Tamy

9

尝试

codecs.encode()

这可以在Python2.7.12和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


2021年3月的工作。谢谢。 - Paisal

1

对于Python3,这是我解决它的方法。

import codecs
import hmac

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接