Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数,也叫做摘要算法。
安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。
如SHA-1、SHA-2(包括SHA-224、SHA-256、SHA-384,和SHA-512四种)。
MD4(RFC 1320)是 MIT 的Ronald L. Rivest在 1990 年设计的,MD 是 Message Digest(消息摘要) 的缩写。它适用在32位字长的处理器上用高速软件实现——它是基于 32位操作数的位操作来实现的。
MD5(RFC 1321)是 Rivest 于1991年对MD4的改进版本。它对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。MD5比MD4来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。
一个优秀的Hash算法具有如下特点:
通过将需要传输的数据进行hash校验,然后在传输之后本地再次对数据进行hash校验,将两次数据对比,即可快速的确认数据是否一致,以及数据是否被篡改。
通过hash算法,可以将明文的密码,加密成固定长度的密码串,大大加强了密码的安全防护,很难暴力破解。
Hash算法也是现代密码体系中的一个重要组成部分。由于非对称算法的运算速度较慢,所以在数字签名协议中,单向散列函数扮演了一个重要的角色。对Hash值,又称"数字摘要"进行数字签名,在统计上可以认为与对文件本身进行数字签名是等效的。
python内置的hash()函数可以快速的获取到hash,不过存在不同进程或者编译过程会有差异的问题,此方法适用于同一次运行时获取制定数据所需要的内容,但不适用于快程序或者跨进程的对比。
测试代码如下:
rawdata = "我只是一个测试数据"
rawdata1 = "我只是一个测试数据 "
print("Hash-rawdata : ",hex(hash(rawdata)))
print("Hash-rawdata : ",hex(hash(rawdata)))
print("Hash-rawdata : ",hex(hash(rawdata)))
print("Hash-rawdata1 : ",hex(hash(rawdata1)))
print("Hash-rawdata1 : ",hex(hash(rawdata1)))
print("Hash-rawdata1 : ",hex(hash(rawdata1)))
多次运行,结果如下:
# 第一次运行
thon\debugpy\adapter/../..\debugpy\launcher' '53510' '--' 'D:\Zero.App\PyQt.Demo\zero.hash\hsah.py'
Hash-rawdata : 0x2dbaf7fe96a1d731
Hash-rawdata : 0x2dbaf7fe96a1d731
Hash-rawdata : 0x2dbaf7fe96a1d731
Hash-rawdata1 : -0x4a06373e0e525a49
Hash-rawdata1 : -0x4a06373e0e525a49
Hash-rawdata1 : -0x4a06373e0e525a49
# 第二次 运行
(venv) PS D:\Zero.App\PyQt.Demo> d:; cd 'd:\Zero.App\PyQt.Demo'; & 'd:\Zero.App\PyQt.Demo\venv\Scripts\python.exe' 'c:\Users\LJM\.vscode\extensions\ms-python.python-2023.16.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '53519' '--' 'D:\Zero.App\PyQt.Demo\zero.hash\hsah.py'
Hash-rawdata : -0x62b9a6623c3140b
Hash-rawdata : -0x62b9a6623c3140b
Hash-rawdata : -0x62b9a6623c3140b
Hash-rawdata1 : 0x7c7ad2277463d6c3
Hash-rawdata1 : 0x7c7ad2277463d6c3
Hash-rawdata1 : 0x7c7ad2277463d6c3
如上,根据多次运行获取到的hash值对比特性汇总如下:
使用hashlib库,可以有效的避免直接使用hash()函数,存在不同进程计算值差异的问题。
rawdata = "我只是一个测试数据"
hash = hashlib.md5(rawdata.encode()) # 注意传入的数据需要是二进制bytes类型数据。
print("Hashlib使用hashlib.md5(rawdata) :")
print("type : ", type(hash)) # 数据类型
print("digest : ", hash.digest()) # 返回数据值
print("hexdigest: ", hash.hexdigest()) # 返回十六进制的摘要哈希值
结果:
Hashlib使用hashlib.md5(rawdata) :
type : <class '_hashlib.HASH'> # 类型,返回的是一个Hash类型
digest : b'\x93laB<*\x97\x84\x11\x94\xf9\x98\xcaY\xcb\xc6'
hexdigest: 936c61423c2a97841194f998ca59cbc6 # 十六机制的值
常用的算法如下,可以使用构造器直接使用。
elif sys.version_info >= (3, 8):
def new(name: str, data: ReadableBuffer = b"") -> _Hash: ...
def md5(string: ReadableBuffer = b"") -> _Hash: ...
def sha1(string: ReadableBuffer = b"") -> _Hash: ...
def sha224(string: ReadableBuffer = b"") -> _Hash: ...
def sha256(string: ReadableBuffer = b"") -> _Hash: ...
def sha384(string: ReadableBuffer = b"") -> _Hash: ...
def sha512(string: ReadableBuffer = b"") -> _Hash: ...
可以使用new()方法使用调用未创建构造器的算法。
print("使用new方法调用算法 :")
h = hashlib.new('sha512_256')
h.update(b"test")
print("hexdigest : ", h.hexdigest())
结果如下:
使用new方法调用算法 :
hexdigest : 3d37fe58435e0d87323dee4a2c1b339ef954de63716ee79f5747f94d974f913f
返回一个集合,其中包含此模块在所有平台上都保证支持的哈希算法的名称。
print("打印所有平台都支持的算法名称:")
print(hashlib.algorithms_guaranteed)
打印所有平台都支持的算法名称:
{'sha384', 'sha3_384', 'md5', 'sha224', 'blake2s', 'sha3_224', 'shake_256', 'shake_128', 'blake2b', 'sha3_512', 'sha1', 'sha3_256', 'sha256', 'sha512'}
算法选用时建议使用通用性较强的算法,当然可以根据自己实际需求选择加密能力更强的。
返回一个集合,其中包含在所运行的 Python 解释器上可用的哈希算法的名称。
print("打印所运行的python解释器支持算法名称:")
print(hashlib.algorithms_available)
打印所运行的python解释器支持算法名称:
{'sha512_224', 'whirlpool', 'sha256', 'shake_128', 'ripemd160', 'blake2s', 'sha3_224', 'sha3_384', 'mdc2', 'sha384', 'md4', 'sha224', 'md5', 'sha1', 'sm3', 'sha512', 'sha3_256', 'sha3_512', 'blake2b', 'md5-sha1', 'shake_256', 'sha512_256'}
返回当前已传给 update() 方法的数据摘要。 这是一个大小为 digest_size 的字节串对象,字节串中可包含 0 至 255 的完整取值范围。
类似于 digest() 但摘要会以两倍长度字符串对象的形式返回,其中仅包含十六进制数码。 这可以被用于在电子邮件或其他非二进制环境中安全地交换数据值。
用 bytes-like object 来更新哈希对象。 重复调用相当于单次调用并传入所有参数的拼接结果: m.update(a); m.update(b) 等价于 m.update(a+b)。
print("使用update更新数据")
hash = hashlib.sha256()
hash.update(b"hello")
print(hash.hexdigest())
hash.update(b"word")
print(hash.hexdigest())
结果如下:
使用update更新数据
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
0b322d15ea034793a8646baa1744ffacbdf7f959b66c68970f032c4ac8b9c8cb