Python学习之路(30)——hashlib模块

时间:2022-03-12 22:38:39

在Python 3中,hashlib模块提供了常见的摘要算法,用于加密相关的操作,代替了Python 2的md5模块和sha模块,主要提供SHA1,SHA224,SHA256,SHA384,SHA512和MD5算法。

 

一、什么是摘要算法?

摘要算法又称为哈希算法、散列算法。通过一个函数,把任意长度的数据转换为一个长度固定(通常用一定长度的16进制)的字符串。

摘要算法有以下重要特征:

  • 只要源数据不同,计算得到的结果,必然不同;
  • 无法从结果反推出源数据(加密不可逆)

摘要算法主要用于对比信息源是否一致,只要源发生变化,得到的摘要必然不同,而相同的源,通过同样的算法得到的结果必然一样。而且由于得到的结果通常比源数据短很多,所以称为“摘要”。

 

二、主要应用场景

1、md5加密

>>> import hashlib
>>> md5_1 = hashlib.md5()
>>> md5_1.update('hello world!'.encode('utf-8'))
>>> print(md5_1.hexdigest())
fc3ff98e8c6a0d3087d515c0473f8677
>>>
>>>
>>> md5_2 = hashlib.md5()
>>> md5_2.update('hello '.encode('utf-8'))
>>> md5_2.update('world!'.encode('utf-8'))
>>> print(md5_2.hexdigest())
fc3ff98e8c6a0d3087d515c0473f8677
>>>
>>>
>>> md5_3 = hashlib.md5()
>>> md5_3.update('hello world!'.encode('utf-8'))
>>> print(md5_3.hexdigest())
fc3ff98e8c6a0d3087d515c0473f8677

1)只要源数据不变,计算得到的md5值都是一样的;

2)如果源数据量很大,可以多次调用update(),最后计算的结果是一样的;

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

 

2、sha1加密

>>> hash_sha1 = hashlib.sha1()
>>> hash_sha1.update('hello python'.encode('utf-8'))
>>> print(hash_sha1.hexdigest())
1d1244e0f2efb8c94ec7a0814568216637b329ca

sha1算法得到的结果是160 bit字节,通常用40位16进制字符串表示。

 

3、比sha1更安全的算法是sha256、sha384和sha512,但是越安全的算法不仅计算越慢,且摘要长度越长,分别为64位、96位和128位。

>>> hash_sha256 = hashlib.sha256()
>>> hash_sha256.update('hello python'.encode('utf-8'))
>>> print(hash_sha256.hexdigest())
373a23512f9531ad49ec6ad43ecdda58df01e59d9ec5812d601fd05cc53345d3
>>> len(hash_sha256.hexdigest())
64
>>>
>>> hash_sha384 = hashlib.sha384()
>>> hash_sha384.update('hello python'.encode('utf-8'))
>>> print(hash_sha384.hexdigest())
eaf44aabb38c34ff948e714b9c3c08f3fed996d74fc9f1225d7368ccb386465df272e55912041921eddf13b51aff833c
>>> len(hash_sha384.hexdigest())
96
>>>
>>> hash_sha512 = hashlib.sha512()
>>> hash_sha512.update('hello python'.encode('utf-8'))
>>> print(hash_sha512.hexdigest())
e8b430766d68a185db5de94b8cf811761db8dd875cf3baae24c6939b0111d19143da1c51294a2aff6adfb46f4687f78ffffa17f2189ca2c0a03a19d4d6bd0d8c
>>> len(hash_sha512.hexdigest())
128

  

4、“加盐”加密

什么是“盐”?

Python学习之路(30)——hashlib模块

以上是*中对于密码学中的“盐”salt的定义。

简单讲一下对于加密的历史:

1)最开始的时候,密码保存在数据库里都是明文的,一般不对密码加密,这样一旦数据库泄露,那么所有的密码都会泄露。

2)为了规避这种设计,人们不再数据库中存储明文密码了,转而存储加密后的密码,典型的加密算法就是上面介绍的MD5和SHA1。由于加密是不可逆(无法解密),所以即使数据库泄露,其密码都是密文,无法得到原始密码,后果也不算太严重。

3)但是,又有人们收集常用的密码,然后对其进行MD5或SHA1计算,得到的结果做成一个数据量庞大的数据字典,然后对泄露的数据库中的密文进行一一对比(暴力破解),如果很不幸的,原始密码包含在这个数据字典中,那么密码就泄露了——称之为“撞库”。

如果设计,使得撞库的概率变小呢?

人们想到了在源密码中的任意位置插入特定的字符串(“加盐”),且这个“盐”必须是随机产生的,也就是说每个面里的salt都不一样,这样计算后得到的密文“撞库”的概率就小到可以忽略不计了,大大提高了安全性。

>>> ########## md5 加密 ##########
>>> hash_md5 = hashlib.md5('hello python'.encode('utf-8'))
>>> hash_md5.update('I am nicolas'.encode('utf-8'))
>>> hash_md5.hexdigest()
'038b7aedf94ada9bbb3adf75851d85fb'
>>>
>>> ########## sha1 加密 ##########
>>> hash_sha1 = hashlib.sha1('hello python'.encode('utf-8'))
>>> hash_sha1.update('I am nicolas'.encode('utf-8'))
>>> hash_sha1.hexdigest()
'696d64ddf6d890c8aed65aae3db1bd5a5603944f'

使用update()对加密算法中添加自定义key再做一次加密。

 

5、hmac加密

hmac内部对创建的key和源数据内容进行处理后再加密

>>> import hmac
>>> h = hmac.new('hello python'.encode('utf-8'))
>>> h.update('nicolas'.encode('utf-8'))
>>> h.hexdigest()
'075b845f4ff164faeb676b8eed70fd7e'

  

6、获取文件的MD5值

import hashlib, os
def md5sum(filename):
"""
用于获取文件的md5值
:param filename: 文件名
:return: MD5码
"""
if not os.path.isfile(filename): # 如果校验md5的文件不是文件,返回空
return
myhash = hashlib.md5()
f = open(filename, 'rb')
while True:
b = f.read(4096)
if not b:
break
myhash.update(b)
f.close()
return myhash.hexdigest()

if __name__ == '__main__':
print(md5sum(r'C:\Python35\LICENSE111.txt'))
print(md5sum(r'C:\Python35\LICENSE.txt'))

 

最后用两个程序总结一下:

示例1:

# -*- coding: utf-8 -*-
'''
通过db字典暴力破解3个用户的密码
'''

import hashlib
def get_md5(s):
return hashlib.md5(s.encode('utf-8')).hexdigest()

class User(object):
def __init__(self, username, password):
self.username = username
self.password = password

db = {
'michael': User('michael', 'e10adc3949ba59abbe56e057f20f883e'),
'bob': User('bob', '878ef96e86145580c38c87f0410ad153'),
'alice': User('alice', '99b1c2188db85afee403b1536010c2c9')
}

def login(username, password):
user = db[username]
return user.password == get_md5(password)

print(login('michael', '123456'))
print(login('bob', 'abc999'))
print(login('alice', 'alice2008'))

##############
执行结果:
True
True
True

  

示例2:

# -*- coding: utf-8 -*-
'''
加盐加密
'''

import hashlib, random

def get_md5(s):
return hashlib.md5(s.encode('utf-8')).hexdigest()

class User(object):
def __init__(self, username, password):
self.username = username
self.salt = ''.join([chr(random.randint(48, 122)) for i in range(20)])
self.password = get_md5(password + self.salt)
db = {
'michael': User('michael', 'e10adc3949ba59abbe56e057f20f883e'),
'bob': User('bob', '878ef96e86145580c38c87f0410ad153'),
'alice': User('alice', '99b1c2188db85afee403b1536010c2c9')
}

def login(username, password):
user = db[username]
return user.password == get_md5(password)


print(bool(login('michael', '123456')))
print(bool(login('bob', 'abc999')))
print(bool(login('alice', 'alice2008')))


########
执行结果
False
False
False