python hashlib库(MD5,sha1,sha256,sha512,pbkdf2_hmac)用法及pbkdf2原理

1 python hashlib 库

Python 的 hashlib 提供了常见的摘要算法,如 MD5,SHA1 等等。摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串。

摘要函数是一个单向函数,通过摘要函数 f() 计算 f(data) 很容易,但通过结果反推 data 非常困难。而且,对原始数据做一个 bit 的修改,都会导致计算出的摘要完全不同。

1.1 md5

MD5 是最常见的摘要算法,速度很快,生成结果是固定的 128 bit 字节,通常用一个 32 位的 16 进制字符串表示。

# coding=utf8
import hashlib

x = hashlib.md5()
x.update('I_love_python'.encode('utf-8'))
print("x1 = ", x.hexdigest())

# 如果数据量很大,可以分块多次调用 update(),最后计算的结果是一样的
x = hashlib.md5()
x.update('I_love_'.encode('utf-8'))
x.update('python'.encode('utf-8'))
print("x2 = ", x.hexdigest())

运行结果:

x1 =  e5030aa9394f5f2da0f0bdf9ea55f532
x2 =  e5030aa9394f5f2da0f0bdf9ea55f532

1.2 sha1

调用 SHA1 和调用 MD5 完全类似,SHA1 的结果是160 bit 字节,通常用一个 40 位的 16 进制字符串表示。

import hashlib

md5 = hashlib.sha1()
md5.update('I_love_'.encode('utf-8'))
md5.update('python'.encode('utf-8'))
print(md5.hexdigest())

1.3 sha256

import hashlib

x = hashlib.sha256()
x.update(b"I_love_python")
print("x_1 = " + x.hexdigest())

x = hashlib.sha256()
x.update("I_love_python".encode())
print("x_2 = " + x.hexdigest())

x = hashlib.sha256()
x.update(b"I_")
x.update(b"love_")
x.update(b"python")
print("x_3 = " + x.hexdigest())

y = hashlib.sha256(b"I_love_python").hexdigest()
print("y_1 = " + y)

z = hashlib.new("sha256")
z.update(b"I_love_python")
print("z_1 = " + z.hexdigest())

运行结果:

x_1 = b770909b3279c6a2dd2c8a9e6376c56e1d0610a26d561f16f25119d0cf9c63f8
x_2 = b770909b3279c6a2dd2c8a9e6376c56e1d0610a26d561f16f25119d0cf9c63f8
x_3 = b770909b3279c6a2dd2c8a9e6376c56e1d0610a26d561f16f25119d0cf9c63f8
y_1 = b770909b3279c6a2dd2c8a9e6376c56e1d0610a26d561f16f25119d0cf9c63f8
z_1 = b770909b3279c6a2dd2c8a9e6376c56e1d0610a26d561f16f25119d0cf9c63f8

1.4 sha512

sha512 算法用起来和前面讲的 MD5,sha1,sha256 算法一样,都是同样的接口

import hashlib

sha512 = hashlib.sha512()
sha512.update("I_love_python".encode('utf-8'))
res = sha512.hexdigest()
print("res = %s" % res)

运行结果:

res = f5d86ab81d53f7a0e4f673fc942522ae22e032234c1c7d6dd57bd534c3efe1db2b06853a7e58ac02bcfe5c901c765511cf2e0492a4d435dbf679403e66859295

SHA256 算法比 SHA1 算法更安全,SHA512 算法比 SHA1 算法更安全,越安全的算法计算速度越慢,摘要更长

1.5 pbkdf2_hmac

hashlib.pbkdf2_hmac 加盐:

import hashlib
import binascii
import os

x = hashlib.pbkdf2_hmac("sha256", b"I_love_python", b"", 1)
print("x_1 = " + binascii.hexlify(x).decode())

x = hashlib.pbkdf2_hmac("sha256", b"I_love_python", b"", 1)  # 相同盐值
print("x_2 = " + binascii.hexlify(x).decode())

x = hashlib.pbkdf2_hmac("sha256", b"I_love_python", b"", 10)  # 相同盐值,不同迭代次数
print("x_3 = " + binascii.hexlify(x).decode())

x = hashlib.pbkdf2_hmac("sha256", b"I_love_python", b"dsa", 1)  # 不同盐值,相同迭代次数
print("x_4 = " + binascii.hexlify(x).decode())

y = hashlib.pbkdf2_hmac("sha256", b"I_love_python", os.urandom(16), 1)  # 随机生成盐值
print("y_1 = " + binascii.hexlify(y).decode())

运行结果:

x_1 = 6cb68803fd852f149dd40a0ed8e4677a92c690361aee907c4c844dc02e2add55
x_2 = 6cb68803fd852f149dd40a0ed8e4677a92c690361aee907c4c844dc02e2add55
x_3 = 18e22cfd109c318a2194826ed6cd9ababc2d567550fc1a5c2d0df6b0b94c4864
x_4 = 340a813fea20130a8895f0727d3a946091e6bdc80f70273c4782b8d390cf2900
y_1 = 9c7e8627b5f5d8a3d6de200578a1fd7033d49eb5e059ee1a110fac3b04f81767

hashlib.pbkdf2_hmac 第一个参数是哈希函数,这里使用的是 sha256,前面讲过的哈希函数都可以使用,比如将第一个参数改为 “sha512”

2 PBKDF2 函数原理

2.1 PBKDF2 介绍

PBKDF2(Password-Based Key Derivation Function) 是一个用来导出密钥的函数,常用于生成加密的密码。

它的基本原理是通过一个伪随机函数(例如 HMAC 函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥。

如果重复的次数足够大,破解的成本就会变得很高。而盐值的添加也会增加“彩虹表”攻击的难度。

2.2 PBKDF2 函数的定义

DK = PBKDF2(PRF, Password, Salt, c, dkLen) 
  • PRF 是一个伪随机函数,例如 HASH_HMAC 函数,它会输出长度为 hLen 的结果。
  • Password 是用来生成密钥的原文密码。
  • Salt 是一个加密用的盐值。
  • c 是进行重复计算的次数。
  • dkLen 是期望得到的密钥的长度。
  • DK 是最后产生的密钥。

2.3 PBKDF2 算法流程

DK 的值由一个以上的 block 拼接而成。block 的数量是 dkLen/hLen 的值。就是说如果 PRF 输出的结果比期望得到的密钥长度要短,则要通过拼接多个结果以满足密钥的长度:

DK = T1 || T2 || ... || Tdklen/hlen

而每个 block 则通过则通过函数F得到:

Ti = F(Password, Salt, c, i)

在函数 F 里,PRF会进行 c 次的运算,然后把得到的结果进行异或运算,得到最终的值。

F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc

第一次,PRF 会使用 Password 作为 key,Salt 拼接上编码成大字节序的 32 位整型的i作为盐值进行运算。

U1 = PRF(Password, Salt || INT_32_BE(i))

而后续的 c-1 次则会使用上次得到的结果作为盐值。

U2 = PRF(Password, U1)
...
Uc = PRF(Password, Uc-1)

函数 F 大致的流程图如下:

在这里插入图片描述
参考:
https://segmentfault.com/a/1190000004261009
https://www.liaoxuefeng.com/wiki/1016959663602400/1017686752491744
https://blog.csdn.net/qq_42486920/article/details/80836749

猜你喜欢

转载自blog.csdn.net/happyjacob/article/details/110771794