Python常用模块之hashlib
摘要算法简介
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。
什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
举个例子,你写了一篇文章,内容是一个字符串'how to use python hashlib - by Michael'
,并附上这篇文章的摘要是'2d73d4f15c0db7f5ecb321b6a65e5d6d'
。如果有人篡改了你的文章,并发表为'how to use python hashlib - by Bob'
,你可以一下子指出Bob篡改了你的文章,因为根据'how to use python hashlib - by Bob'
计算出的摘要不同于原始文章的摘要。
可见,摘要算法就是通过摘要函数f()
对任意长度的数据data
计算出固定长度的摘要digest
,目的是为了发现原始数据是否被人篡改过。
摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)
很容易,但通过digest
反推data
却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
Python里面的hashlib模块提供了很多加密的算法,这里介绍一下hashlib的简单使用事例,用hashlib的md5算法加密数据
1 import hashlib 2 3 4 hash = hashlib.md5()#md5对象,md5不能反解,但是加密是固定的,就是关系是一一对应,所以有缺陷,可以被对撞出来 5 hash.update(bytes('admin',encoding='utf-8'))#要对哪个字符串进行加密,就放这里 6 print(hash.hexdigest())#拿到加密字符串 7 # hash2=hashlib.sha384()#不同算法,hashlib很多加密算法 8 # hash2.update(bytes('admin',encoding='utf-8')) 9 # print(hash.hexdigest()) 10 11 12 hash3 = hashlib.md5(bytes('abd',encoding='utf-8')) 13 ''' 如果没有参数,所以md5遵守一个规则,生成同一个对应关系,如果加了参数, 14 就是在原先加密的基础上再加密一层,这样的话参数只有自己知道,防止被撞库, 15 因为别人永远拿不到这个参数 16 ''' 17 hash3.update(bytes('admin',encoding='utf-8')) 18 print(hash3.hexdigest
1 m=hashlib.md5() 2 3 print(m) 4 5 m.update('hello world'.encode('utf-8')) 6 7 print(m.hexdigest()) #用十六进制的方式取出加密后的信息 8 #print(m.digest()) #用二进制的方式取出加密后的信息 9 ''' 10 5eb63bbbe01eeed093cb22bb8f5acdc3 11 b'^\xb6;\xbb\xe0\x1e\xee\xd0\x93\xcb"\xbb\x8fZ\xcd\xc3' 12 ''' 13 m.update('Tom'.encode('utf-8')) 14 print("分两次装换的结果:",m.hexdigest()) 15 16 17 md=hashlib.md5() 18 md.update('hello worldTom'.encode('utf-8')) 19 print("和一起转换的结果:",md.hexdigest())
运行结果:
"D:\Program Files (x86)\python36\python.exe" F:/python从入门到放弃/7.3/hashlib_mudol.py <md5 HASH object @ 0x0000000001DDC418> 5eb63bbbe01eeed093cb22bb8f5acdc3 b'^\xb6;\xbb\xe0\x1e\xee\xd0\x93\xcb"\xbb\x8fZ\xcd\xc3' 分两次装换的结果: 47cc4aba78c77a9d7e2e68c5f86999ab 和一起转换的结果: 47cc4aba78c77a9d7e2e68c5f86999ab Process finished with exit code 0
MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。
另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:
import hashlib sha1 = hashlib.sha1() sha1.update('how to use sha1 in '.encode('utf-8')) sha1.update('python hashlib?'.encode('utf-8')) print(sha1.hexdigest())
SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。
比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法不仅越慢,而且摘要长度更长。
有没有可能两个不同的数据通过某个摘要算法得到了相同的摘要?完全有可能,因为任何摘要算法都是把无限多的数据集合映射到一个有限的集合中。这种情况称为碰撞,比如Bob试图根据你的摘要反推出一篇文章'how to learn hashlib in python - by Bob'
,并且这篇文章的摘要恰好和你的文章完全一致,这种情况也并非不可能出现,但是非常非常困难。
摘要算法应用
摘要算法能应用到什么地方?举个常用例子:
任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:
name | password |
---|---|
michael | 123456 |
bob | abc999 |
alice | alice2008 |
如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。
正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:
username | password |
---|---|
michael | e10adc3949ba59abbe56e057f20f883e |
bob | 878ef96e86145580c38c87f0410ad153 |
alice | 99b1c2188db85afee403b1536010c2c9 |
当用户登录时,首先计算用户输入的明文口令的MD5,然后和数据库存储的MD5对比,如果一致,说明口令输入正确,如果不一致,口令肯定错误。
练习
根据用户输入的口令,计算出存储在数据库中的MD5口令:
def calc_md5(password): pass
存储MD5的好处是即使运维人员能访问数据库,也无法获知用户的明文口令。
设计一个验证用户登录的函数,根据用户输入的口令是否正确,返回True或False:
#-*- coding:utf-8 -*- import hashlib db = { 'michael': 'e10adc3949ba59abbe56e057f20f883e', 'bob': '878ef96e86145580c38c87f0410ad153', 'alice': '99b1c2188db85afee403b1536010c2c9' } def login(user, password): md=hashlib.md5() md.update(password.encode('utf-8')) if md.hexdigest()==db[user]: return True else: return False #m.update('Tom'.encode('utf-8')) # 测试: assert login('michael', '123456') assert login('bob', 'abc999') assert login('alice', 'alice2008') assert not login('michael', '1234567') assert not login('bob', '123456') assert not login('alice', 'Alice2008') print('ok')
运行结果:
"D:\Program Files (x86)\python36\python.exe" F:/python从入门到放弃/7.3/hashlib_mudol.py ok Process finished with exit code 0
练习:
写一个利用md5进行用户登陆网站进行注册之后密码加密的基本事例:
1 #-*- coding:utf-8 -*- 2 3 import hashlib 4 5 #用字典模拟一个数据库,用来存储用户信息。 6 db={} 7 8 def md5(pwd): #这是一个加密函数,将传进来的函数加密 9 md5_pwd=hashlib.md5() 10 md5_pwd.update(pwd.encode('utf-8')) 11 return md5_pwd.hexdigest() #返回加密的数据 12 13 def log(user,pwd): #登陆时候时候的函数,由于md5不能反解,因此登陆的时候用正解 14 if user in db: 15 if md5(pwd)==db[user]: 16 print('登陆成功') 17 else: 18 print("登陆失败,没有该用户,请先注册") 19 20 def register(user,pwd):#注册的时候把用户名和加密的密码写进数据库,保存起来 21 if user in db: 22 print('数据库已存在该用户,请选择登陆窗口') 23 else: 24 db[user]=md5(pwd) 25 print('注册成功。请通过登陆窗口登陆') 26 27 while True: 28 i = input('1表示登陆,2表示注册:') 29 if i=='1': 30 user=input("用户名:") 31 pwd=input("密码:") 32 log(user,pwd) 33 elif i=='2': 34 user = input("用户名:") 35 pwd = input("密码:") 36 register(user,pwd) 37 else: 38 print("打印出数据库所有用户的账户和口令") #用来查看注册在字典里的数据 39 print(db)
运行结果:
"D:\Program Files (x86)\python36\python.exe" F:/python从入门到放弃/7.3/hashlib_mudol.py 1表示登陆,2表示注册:2 用户名:Tom 密码:123 注册成功。请通过登陆窗口登陆 1表示登陆,2表示注册:1 用户名:Tom 密码:123 登陆成功 1表示登陆,2表示注册:1 用户名:May 密码:123 登陆失败,没有该用户,请先注册 1表示登陆,2表示注册:2 用户名:Jack 密码:234 注册成功。请通过登陆窗口登陆 1表示登陆,2表示注册:3 打印出数据库所有用户的账户和口令 {'Tom': '202cb962ac59075b964b07152d234b70', 'Jack': '289dff07669d7a23de0ef88d2f7129e7'} 1表示登陆,2表示注册: Process finished with exit code 1
正确的保存口令的方式是不存储用户的明文口令
python 还有一个 hmac 模块,它内部对我们创建 key 和 内容 再进行处理然后再加密
散列消息鉴别码,简称HMAC,是一种基于消息鉴别码MAC(Message Authentication Code)的鉴别机制。使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥K来鉴别消息的真伪;
一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。
import hmac h = hmac.new(b'天王盖地虎', b'宝塔镇河妖') print h.hexdigest()