hashlib — 安全哈希和消息摘要 — Python 文档
hashlib — 安全散列和消息摘要
该模块为许多不同的安全散列和消息摘要算法实现了一个通用接口。 包括 FIPS 安全哈希算法 SHA1、SHA224、SHA256、SHA384 和 SHA512(在 FIPS 180-2 中定义)以及 RSA 的 MD5 算法(在 Internet RFC 1321中定义) . 术语“安全哈希”和“消息摘要”可以互换。 较旧的算法称为消息摘要。 现代术语是安全哈希。
警告
一些算法已知散列冲突的弱点,请参阅最后的“另请参阅”部分。
哈希算法
每种类型的 hash 都有一个构造函数方法。 都返回一个具有相同简单接口的哈希对象。 例如:使用 sha256()
创建一个 SHA-256 哈希对象。 您现在可以使用 update()
方法为该对象提供 字节类对象 (通常为 字节 )。 在任何时候,您都可以使用 digest()
或 hexdigest()
方法要求它提供迄今为止馈送给它的数据串联的 digest。
笔记
不支持将字符串对象送入 update()
,因为哈希对字节起作用,而不是对字符起作用。
此模块中始终存在的哈希算法的构造函数是 sha1()
、sha224()
、sha256()
、sha384()
、sha512()
、 blake2b() 和 blake2s()。 md5()
通常也可用,但如果您使用的是罕见的“FIPS 兼容”Python 版本,它可能会丢失。 其他算法也可能可用,具体取决于 Python 在您的平台上使用的 OpenSSL 库。 在大多数平台上,sha3_224()
、sha3_256()
、sha3_384()
、sha3_512()
、shake_128()
、shake_256()
也可用。
3.6 版新功能:SHA3 (Keccak) 和 SHAKE 构造函数 sha3_224()
、sha3_256()
、sha3_384()
、sha3_512()
、[ X114X]、shake_256()
。
例如,要获取字节串 b'Nobody inspects the spammish repetition'
的摘要:
>>> import hashlib
>>> m = hashlib.sha256()
>>> m.update(b"Nobody inspects")
>>> m.update(b" the spammish repetition")
>>> m.digest()
b'\x03\x1e\xdd}Ae\x15\x93\xc5\xfe\\\x00o\xa5u+7\xfd\xdf\xf7\xbcN\x84:\xa6\xaf\x0c\x95\x0fK\x94\x06'
>>> m.digest_size
32
>>> m.block_size
64
更浓缩:
>>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest()
'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'
- hashlib.new(name[, data])
- 是一个通用构造函数,它将所需算法的字符串 name 作为其第一个参数。 它还允许访问上面列出的哈希值以及您的 OpenSSL 库可能提供的任何其他算法。 命名构造函数比 new() 快得多,应该是首选。
使用 new() 和 OpenSSL 提供的算法:
>>> h = hashlib.new('sha512_256')
>>> h.update(b"Nobody inspects the spammish repetition")
>>> h.hexdigest()
'19197dc4d03829df858011c6c87600f994a858103bbc19005f20987aa19a97e2'
Hashlib 提供以下常量属性:
- hashlib.algorithms_guaranteed
包含保证在所有平台上由该模块支持的哈希算法名称的集合。 请注意,尽管某些上游供应商提供了一个奇怪的“符合 FIPS 标准”的 Python 构建,但“md5”仍在此列表中。
3.2 版中的新功能。
- hashlib.algorithms_available
包含正在运行的 Python 解释器中可用的哈希算法名称的集合。 这些名称在传递给 new() 时将被识别。 algorithms_guaranteed 将始终是一个子集。 相同的算法可能会以不同的名称多次出现在该集合中(感谢 OpenSSL)。
3.2 版中的新功能。
以下值作为构造函数返回的散列对象的常量属性提供:
- hash.digest_size
- 结果哈希的大小(以字节为单位)。
- hash.block_size
- 哈希算法的内部块大小(以字节为单位)。
散列对象具有以下属性:
- hash.name
这个散列的规范名称,总是小写,总是适合作为 new() 的参数来创建另一个这种类型的散列。
3.4 版更改: name 属性自 CPython 一开始就存在于 CPython 中,但直到 Python 3.4 未正式指定,因此在某些平台上可能不存在。
散列对象具有以下方法:
- hash.update(data)
使用 bytes-like object 更新哈希对象。 重复调用等效于将所有参数串联起来的单个调用:
m.update(a); m.update(b)
等效于m.update(a+b)
。3.1 版更改: 发布 Python GIL 以允许其他线程运行,同时使用 OpenSSL 提供的哈希算法对大于 2047 字节的数据进行哈希更新。
- hash.digest()
- 返回到目前为止传递给 update() 方法的数据的摘要。 这是一个大小为 digest_size 的字节对象,它可能包含从 0 到 255 的整个范围内的字节。
- hash.hexdigest()
- 与 digest() 类似,除了摘要作为双倍长度的字符串对象返回,仅包含十六进制数字。 这可用于在电子邮件或其他非二进制环境中安全地交换值。
- hash.copy()
- 返回哈希对象的副本(“克隆”)。 这可用于有效计算共享公共初始子串的数据摘要。
SHAKE可变长度摘要
shake_128()
和 shake_256()
算法提供可变长度摘要,length_in_bits//2 高达 128 或 256 位的安全性。 因此,他们的摘要方法需要一个长度。 最大长度不受 SHAKE 算法的限制。
- shake.digest(length)
- 返回到目前为止传递给
update()
方法的数据的摘要。 这是一个大小为 length 的字节对象,它可能包含从 0 到 255 的整个范围内的字节。
- shake.hexdigest(length)
- 与 digest() 类似,除了摘要作为双倍长度的字符串对象返回,仅包含十六进制数字。 这可用于在电子邮件或其他非二进制环境中安全地交换值。
密钥推导
密钥派生和密钥拉伸算法专为安全密码散列而设计。 诸如 sha1(password)
之类的幼稚算法无法抵抗蛮力攻击。 一个好的密码散列函数必须是可调的、缓慢的,并且包含一个 salt。
- hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)
该函数提供了基于PKCS#5密码的密钥推导函数2。 它使用 HMAC 作为伪随机函数。
字符串 hash_name 是 HMAC 的哈希摘要算法的所需名称,例如 “sha1”或“sha256”。 password 和 salt 被解释为字节缓冲区。 应用程序和库应该将 password 限制为合理的长度(例如 1024). salt 应该是来自适当来源的大约 16 个或更多字节,例如 os.urandom()。
迭代次数应该根据哈希算法和计算能力来选择。 截至 2013 年,建议至少进行 100,000 次 SHA-256 迭代。
dklen 是派生密钥的长度。 如果 dklen 是
None
则使用散列算法 hash_name 的摘要大小,例如 SHA-512 为 64。>>> import hashlib >>> dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000) >>> dk.hex() '0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5'
3.4 版中的新功能。
笔记
pbkdf2_hmac 的快速实现可用于 OpenSSL。 Python 实现使用 hmac 的内联版本。 它大约慢了三倍,并且不会释放 GIL。
- hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)
该函数提供了 RFC 7914 中定义的基于 scrypt 密码的密钥派生函数。
password 和 salt 必须是 bytes-like objects。 应用程序和库应该将 password 限制为合理的长度(例如 1024). salt 应该是来自适当来源的大约 16 个或更多字节,例如 os.urandom()。
n 是 CPU/内存成本因子,r 是块大小,p 并行化因子和 maxmem 限制内存(OpenSSL 1.1.0默认为 32 MiB)。 dklen 是派生密钥的长度。
3.6 版中的新功能。
布莱克2
BLAKE2 是在 RFC 7693 中定义的加密哈希函数,它有两种风格:
- BLAKE2b,针对 64 位平台进行了优化,可生成 1 到 64 字节之间任意大小的摘要,
- BLAKE2s,针对 8 到 32 位平台进行了优化,可生成 1 到 32 字节之间任意大小的摘要。
BLAKE2 支持 键控模式 (更快更简单地替代 [X74X]HMAC)、salted hashing、personalization 和 tree hashing[ X161X]。
来自该模块的哈希对象遵循标准库的 hashlib 对象的 API。
创建哈希对象
通过调用构造函数创建新的哈希对象:
- hashlib.blake2b(data=b, *, digest_size=64, key=b, salt=b, person=b, fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False)
- hashlib.blake2s(data=b, *, digest_size=32, key=b, salt=b, person=b, fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False)
这些函数返回相应的哈希对象,用于计算 BLAKE2b 或 BLAKE2s。 他们可以选择采用以下通用参数:
- data:要散列的初始数据块,必须是 bytes-like object。 它只能作为位置参数传递。
- digest_size:输出摘要的大小(以字节为单位)。
- key:键控散列的键(BLAKE2b 最多 64 个字节,BLAKE2s 最多 32 个字节)。
- salt:随机散列的盐(BLAKE2b 最多 16 个字节,BLAKE2s 最多 8 个字节)。
- person:个性化字符串(BLAKE2b 最多 16 个字节,BLAKE2s 最多 8 个字节)。
下表显示了一般参数的限制(以字节为单位):
哈希 | 摘要大小 | 长度(键) | len(盐) | len(人) |
---|---|---|---|---|
布莱克2b | 64 | 64 | 16 | 16 |
BLAKE2s | 32 | 32 | 8 | 8 |
笔记
BLAKE2 规范为 salt 和个性化参数定义了常量长度,但是,为了方便起见,此实现接受任意大小的字节字符串,直到指定长度。 如果参数的长度小于指定的长度,则用零填充,例如,b'salt'
和 b'salt\x00'
是相同的值。 (key 不是这种情况。)
这些尺寸可作为模块 常量 使用,如下所述。
构造函数还接受以下树散列参数:
- fanout:扇出(0 到 255,0 表示无限制,1 顺序模式)。
- depth:树的最大深度(1 到 255,255 无限制,1 顺序模式)。
- leaf_size:叶子的最大字节长度(0 到 2**32-1,0 如果无限制或在顺序模式下)。
- node_offset:节点偏移量(BLAKE2b 为 0 到 2**64-1,BLAKE2s 为 0 到 2**48-1,第一个、最左边、叶子或顺序模式为 0)。
- node_depth:节点深度(0 到 255,0 表示叶子,或在顺序模式下)。
- inner_size:内部摘要大小(BLAKE2b 为 0 到 64,BLAKE2s 为 0 到 32,顺序模式为 0)。
- last_node:布尔值,指示处理的节点是否是最后一个(False 顺序模式)。
请参阅 BLAKE2 规范 中的第 2.10 节以全面了解树哈希。
常数
- blake2b.SALT_SIZE
- blake2s.SALT_SIZE
盐长度(构造函数接受的最大长度)。
- blake2b.PERSON_SIZE
- blake2s.PERSON_SIZE
个性化字符串长度(构造函数接受的最大长度)。
- blake2b.MAX_KEY_SIZE
- blake2s.MAX_KEY_SIZE
最大密钥大小。
- blake2b.MAX_DIGEST_SIZE
- blake2s.MAX_DIGEST_SIZE
散列函数可以输出的最大摘要大小。
例子
简单的散列
要计算某些数据的散列,您应该首先通过调用适当的构造函数(blake2b() 或 blake2s())构造一个散列对象,然后通过调用用数据更新它update()
在对象上,最后,通过调用 digest()
(或 hexdigest()
为十六进制编码字符串)从对象中获取摘要。
>>> from hashlib import blake2b
>>> h = blake2b()
>>> h.update(b'Hello world')
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'
作为一种快捷方式,您可以将要更新的第一块数据作为位置参数直接传递给构造函数:
>>> from hashlib import blake2b
>>> blake2b(b'Hello world').hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'
您可以根据需要多次调用 hash.update() 来迭代更新哈希:
>>> from hashlib import blake2b
>>> items = [b'Hello', b' ', b'world']
>>> h = blake2b()
>>> for item in items:
... h.update(item)
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'
使用不同的摘要大小
BLAKE2 具有可配置的摘要大小,BLAKE2b 最多 64 个字节,BLAKE2s 最多 32 个字节。 例如,要在不改变输出大小的情况下用 BLAKE2b 替换 SHA-1,我们可以告诉 BLAKE2b 生成 20 字节的摘要:
>>> from hashlib import blake2b
>>> h = blake2b(digest_size=20)
>>> h.update(b'Replacing SHA1 with the more secure function')
>>> h.hexdigest()
'd24f26cf8de66472d58d4e1b1774b4c9158b1f4c'
>>> h.digest_size
20
>>> len(h.digest())
20
具有不同摘要大小的散列对象具有完全不同的输出(较短的散列是 不是较长散列的 前缀); 即使输出长度相同,BLAKE2b 和 BLAKE2s 也会产生不同的输出:
>>> from hashlib import blake2b, blake2s
>>> blake2b(digest_size=10).hexdigest()
'6fa1d8fcfd719046d762'
>>> blake2b(digest_size=11).hexdigest()
'eb6ec15daf9546254f0809'
>>> blake2s(digest_size=10).hexdigest()
'1bf21a98c78a1c376ae9'
>>> blake2s(digest_size=11).hexdigest()
'567004bf96e4a25773ebf4'
键控散列
密钥散列可用于身份验证,作为 基于哈希的消息身份验证代码 (HMAC) 的更快、更简单的替代品。 由于继承自 BLAKE 的不可区分性,BLAKE2 可以安全地用于前缀 MAC 模式。
此示例显示如何使用密钥 b'pseudorandom key'
获取消息 b'message data'
的(十六进制编码)128 位身份验证代码:
>>> from hashlib import blake2b
>>> h = blake2b(key=b'pseudorandom key', digest_size=16)
>>> h.update(b'message data')
>>> h.hexdigest()
'3d363ff7401e02026f4a4687d4863ced'
作为一个实际示例,Web 应用程序可以对发送给用户的 cookie 进行对称签名,然后对其进行验证以确保它们没有被篡改:
>>> from hashlib import blake2b
>>> from hmac import compare_digest
>>>
>>> SECRET_KEY = b'pseudorandomly generated server secret key'
>>> AUTH_SIZE = 16
>>>
>>> def sign(cookie):
... h = blake2b(digest_size=AUTH_SIZE, key=SECRET_KEY)
... h.update(cookie)
... return h.hexdigest().encode('utf-8')
>>>
>>> def verify(cookie, sig):
... good_sig = sign(cookie)
... return compare_digest(good_sig, sig)
>>>
>>> cookie = b'user-alice'
>>> sig = sign(cookie)
>>> print("{0},{1}".format(cookie.decode('utf-8'), sig))
user-alice,b'43b3c982cf697e0c5ab22172d1ca7421'
>>> verify(cookie, sig)
True
>>> verify(b'user-bob', sig)
False
>>> verify(cookie, b'0102030405060708090a0b0c0d0e0f00')
False
即使有本机键控散列模式,BLAKE2 当然也可以用于带有 hmac 模块的 HMAC 构造:
>>> import hmac, hashlib
>>> m = hmac.new(b'secret key', digestmod=hashlib.blake2s)
>>> m.update(b'message')
>>> m.hexdigest()
'e3c8102868d28b5ff85fc35dda07329970d1a01e273c37481326fe0c861c8142'
随机散列
通过设置 salt 参数,用户可以为哈希函数引入随机化。 随机散列对于防止对数字签名中使用的散列函数的冲突攻击很有用。
随机散列是为消息准备者一方生成要由另一方(消息签名者)签名的全部或部分消息的情况而设计的。 如果消息准备者能够发现加密散列函数冲突(即,两条消息产生相同的散列值),那么他们可能会准备将产生相同散列值和数字签名但具有不同结果的有意义的消息版本(例如,将 1,000,000 美元转移到一个帐户,而不是 10 美元)。 加密散列函数的设计以抗碰撞性为主要目标,但当前对攻击加密散列函数的集中可能导致给定的加密散列函数提供的抗碰撞性低于预期。 随机散列通过降低准备者在数字签名生成过程中生成最终产生相同散列值的两个或多个消息的可能性,为签名者提供额外的保护——即使找到散列函数的冲突是可行的。 然而,当消息的所有部分都由签名者准备时,使用随机散列可能会降低数字签名提供的安全性。
在 BLAKE2 中,salt 在初始化期间作为哈希函数的一次性输入处理,而不是作为每个压缩函数的输入。
>>> import os
>>> from hashlib import blake2b
>>> msg = b'some message'
>>> # Calculate the first hash with a random salt.
>>> salt1 = os.urandom(blake2b.SALT_SIZE)
>>> h1 = blake2b(salt=salt1)
>>> h1.update(msg)
>>> # Calculate the second hash with a different random salt.
>>> salt2 = os.urandom(blake2b.SALT_SIZE)
>>> h2 = blake2b(salt=salt2)
>>> h2.update(msg)
>>> # The digests are different.
>>> h1.digest() != h2.digest()
True
个性化
有时为了不同的目的,强制散列函数为相同的输入生成不同的摘要是很有用的。 引用 Skein 哈希函数的作者:
我们建议所有应用程序设计人员认真考虑这样做; 我们已经看到许多协议,其中在协议的一部分中计算的散列可以用于完全不同的部分,因为对相似或相关的数据进行了两次散列计算,并且攻击者可以强制应用程序将散列输入作为相同的。 对协议中使用的每个散列函数进行个性化处理,可以立即阻止这种类型的攻击。
(Skein 哈希函数家族 ,第 21)
BLAKE2 可以通过将字节传递给 person 参数来个性化:
>>> from hashlib import blake2b
>>> FILES_HASH_PERSON = b'MyApp Files Hash'
>>> BLOCK_HASH_PERSON = b'MyApp Block Hash'
>>> h = blake2b(digest_size=32, person=FILES_HASH_PERSON)
>>> h.update(b'the same content')
>>> h.hexdigest()
'20d9cd024d4fb086aae819a1432dd2466de12947831b75c5a30cf2676095d3b4'
>>> h = blake2b(digest_size=32, person=BLOCK_HASH_PERSON)
>>> h.update(b'the same content')
>>> h.hexdigest()
'cf68fb5761b9c44e7878bfb2c4c9aea52264a80b75005e65619778de59f383a3'
个性化与键控模式一起也可用于从单个密钥派生不同的密钥。
>>> from hashlib import blake2s
>>> from base64 import b64decode, b64encode
>>> orig_key = b64decode(b'Rm5EPJai72qcK3RGBpW3vPNfZy5OZothY+kHY6h21KM=')
>>> enc_key = blake2s(key=orig_key, person=b'kEncrypt').digest()
>>> mac_key = blake2s(key=orig_key, person=b'kMAC').digest()
>>> print(b64encode(enc_key).decode('utf-8'))
rbPb15S/Z9t+agffno5wuhB77VbRi6F9Iv2qIxU7WHw=
>>> print(b64encode(mac_key).decode('utf-8'))
G9GtHFE1YluXY1zWPlYk1e/nWfu0WSEb0KRcjhDeP/o=
树模式
这是一个散列具有两个叶节点的最小树的示例:
10
/ \
00 01
此示例使用 64 字节内部摘要,并返回 32 字节最终摘要:
>>> from hashlib import blake2b
>>>
>>> FANOUT = 2
>>> DEPTH = 2
>>> LEAF_SIZE = 4096
>>> INNER_SIZE = 64
>>>
>>> buf = bytearray(6000)
>>>
>>> # Left leaf
... h00 = blake2b(buf[0:LEAF_SIZE], fanout=FANOUT, depth=DEPTH,
... leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
... node_offset=0, node_depth=0, last_node=False)
>>> # Right leaf
... h01 = blake2b(buf[LEAF_SIZE:], fanout=FANOUT, depth=DEPTH,
... leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
... node_offset=1, node_depth=0, last_node=True)
>>> # Root node
... h10 = blake2b(digest_size=32, fanout=FANOUT, depth=DEPTH,
... leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
... node_offset=0, node_depth=1, last_node=True)
>>> h10.update(h00.digest())
>>> h10.update(h01.digest())
>>> h10.hexdigest()
'3ad2a9b37c6070e374c7a8c508fe20ca86b6ed54e286e93a0318e95e881db5aa'
学分
布莱克2 是由让-菲利普·奥马松 , 塞缪尔·内维斯 , Zooko Wilcox-O'Hearn , 和克里斯蒂安·温纳莱恩基于 SHA-3 决赛选手布莱克由...制作让-菲利普·奥马松 , 卢卡·亨岑 , 威利·迈耶 , 和拉斐尔 C.-W. 潘 .
它使用来自的核心算法恰恰密码设计者丹尼尔·J。 伯恩斯坦 .
stdlib 实现基于 pyblake2 模块。 它由 Dmitry Chestnykh 基于 Samuel Neves 编写的 C 实现编写。 该文档复制自 pyblake2,由 Dmitry Chestnykh 编写。
Christian Heimes 为 Python 部分重写了 C 代码。
以下公共领域奉献适用于 C 哈希函数实现、扩展代码和本文档:
在法律允许的范围内,作者已将本软件的所有版权和相关和邻接权授予全球公共领域。 该软件的分发没有任何保证。
您应该已经收到 CC0 Public Domain Dedication 的副本以及此软件。 如果没有,请参阅 https://creativecommons.org/publicdomain/zero/1.0/。
根据 Creative Commons Public Domain Dedication 1.0 Universal,以下人员帮助开发或贡献了他们对项目和公共领域的更改:
- 亚历山大·索科洛夫斯基
也可以看看
- 模块 hmac
- 使用哈希生成消息身份验证代码的模块。
- 模块 base64
- 另一种为非二进制环境编码二进制哈希的方法。
- https://blake2.net
- BLAKE2 官方网站。
- https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf
- 关于安全哈希算法的 FIPS 180-2 出版物。
- https://en.wikipedia.org/wiki/Cryptographic_hash_function#Cryptographic_hash_algorithms
- 维基百科文章,其中包含有关哪些算法存在已知问题以及它们的使用意味着什么的信息。
- https://www.ietf.org/rfc/rfc2898.txt
- PKCS #5:基于密码的加密规范 2.0 版