python 密码学示例——理解哈希(Hash)算法


Posted in Python onSeptember 21, 2020

Hash 是密码学安全性的基石,它引入了单向函数(one-way function)和指纹(fingerprint)的概念。即:

  • 对于任意输入,都可以产生相同的、唯一的输出值
  • 输出值中不包含输入值的任何线索

一、保密性(confidentiality)与完整性(integrity)

简单来说,信息的保密性确保除授权人员以外的任何人都无法读取该消息,信息的完整性则确保除授权人员以外的任何人都无法修改该消息。
很多时候一段加密的消息无法被他人读取和理解(保密性),并不意味着该密文不会在传播过程中被截取和恶意修改(完整性)。

信息摘要(message digest)或指纹(fingerprint)技术即用于验证信息的完整性。

信息摘要需满足的基本条件为:

  • 相同的文档永远会生成相同的摘要(能够作为身份线索)
  • 生成的摘要“感觉”是随机的,即摘要中不包含原始文档的任何信息(无法被破解)

信息摘要也被称作指纹,即可以代表某份文档“身份”的一小段数据,类似于人类的指纹。
每个人都可以通过指纹验证其身份,但该指纹并不包含其身体的所有信息。文档的指纹也是如此,可以很方便快速的通过文档内容计算得出一小段唯一的指纹数据作为其身份证明,但是只有指纹数据就几乎不可能得出原始文档的内容。

对于两份文档,只需要比对其信息摘要(指纹)是否一致,就可以确保其内容是否相同,在传播过程中是否被人恶意修改。同时该指纹信息也不会造成原始文档本内容的泄露。

二、MD5

MD5 是一种比较古老的哈希算法,其名字中的 MD 即代表 message digest。它可以从任意大小的文档计算出一个唯一的 16 字节长度的摘要数据。

PS:鉴于 MD5 较悠久的历史和不够长的摘要长度,不推荐在安全性很敏感的场景中使用该算法。

>>> from hashlib import md5
>>> md5(b'alice').hexdigest()
'6384e2b2184bcbf58eccf10ca7a6563c'
>>> md5(b'bob').hexdigest()
'9f9d51bc70ef21ca5c14f307980a29d8'
>>> md5(b'balice').hexdigest()
'6760742ebf884c998752b4e082b78224'
>>> md5(b'cob').hexdigest()
'386685f06beecb9f35db2e22da429ec9'
>>> md5(b'a').hexdigest()
'0cc175b9c0f1b6a831c399e269772661'
>>> md5(b'aa').hexdigest()
'4124bc0a9335c27f086f24ba207a4912'
>>> md5(b'aa' * 100000).hexdigest()
'561b1994f6baacd6e5eaf4baaa12849f'
>>> md5(b'alice').hexdigest()
'6384e2b2184bcbf58eccf10ca7a6563c'

从输出中可以看出,针对不同的输入内容(即便相似度很高,比如 bob 和 cob),摘要算法生成的输出是发散的,彼此之间没有相似性,像是随机生成的结果。
但是对于任意相同的输入,生成的摘要数据则都是确定的、唯一的。

三、哈希算法的规则

一般我们提到哈希算法,都会关联到密码学、安全性等场景中,实际上我们很早就接触了一种完全“非密码学”的哈希场景。比如小时候跟老师学习判断一个数是奇数还是偶数。。。
从本质上看,哈希函数的目的是将巨大(甚至无穷大)数量的事物映射到一个相对较小的数据集中。比如 MD5,不管输入的文档有多大,最终都会生成一个固定长度(16 字节)的十六进制数字作为指纹。
这就意味着 MD5 的输入集合,实际上是大于其输出集合的。即只要输入文档的集合足够大(很大很大),就有可能出现重复的指纹信息。

这和判断数字奇偶是相通的。不管某个数字有多大多奇特,我们永远可以将它“压缩”成奇数或偶数,用 1 bit 的 1 或 0 表示就可以。但是只说明某个未知数字是奇数(或偶数),我们就无法猜出该数字的准确值。

上面的逻辑验证了哈希函数共有的 3 个特性:

  • consistency(一致性):相同的输入只会生成相同的输出信息
  • compression(压缩):可以将体量很大的输入压缩成一个固定大小的输出
  • lossiness(有损的):只通过检查输出无法反向计算出输入值

但是对于一个满足密码学安全的哈希函数而言,除以上三点以外还需要具有如下属性:

  • Preimage resistance
  • Second-preimage resistance
  • Collision resistance

Preimage Resistance

哈希函数的 preimage 是指能够生成同一个特定指纹的所有输入的合集。即对于某个哈希函数 H 与摘要 k,所有能够生成 k 的输入值 x (满足 H(x) = k)共同组成了 H 与 k 的 preimage。

preimage resistance 的意义即为,在仅仅只是知晓某个摘要的前提下,通过有限的计算无法获取其 preimage 中的任何一个元素。即只通过结果无法知晓输入。
摘要中不包含原始文档的任何信息(lossiness),无法通过逆向运算的方式由摘要反推出原始输入。只能随机地尝试任意输入,以期碰巧得到同样的摘要信息(暴力破解)。

因此前面提到的奇偶函数就不能作为一个安全的哈希函数使用。假设使用奇偶作为哈希函数(奇数输出 1,偶数输出 0),则对于摘要 1,总可以很轻易的在 preimage(此处是全体奇数)中找到任意多个摘要同为 1 的元素。这意味着原始输入可以轻易被修改而不影响指纹数据,则该指纹作为信息完整性的验证条件就失去了意义。

但是对于较安全的哈希算法如 MD5,由 MD5(x) = ca8a0fb205782051bd49f02eae17c9ee 就无法在有限的计算内找到确定的 x 的值。
MD5 生成 16 字节(16 * 8 = 128bit)长度的摘要,其中可以包含 2^128 种不同的数字组合。因此使用暴力破解的话,最多需要尝试 2^128 = 340282366920938463463374607431768211456 次!
假设每秒钟可以尝试一百万条输入,仍需要 10^26 年完成所有验证操作!

Second-Preimage Resistance 与 Collision Resistance

second-primage resistance 是指即便知晓某个原始文档以及由该文档生成的摘要数据,仍很难计算可以出生成同样摘要的另一个不同的文档。
即在已知 MD5(alice) = 384e2b2184bcbf58eccf10ca7a6563c 的情况下,仍无法找出除 alice 以外的另一个输入生成同样的摘要。为了寻求可以替换掉 alice 的另一个值,同时不影响摘要认证,达到混淆的目的,最终仍需使用暴力破解的方式。

collision resistance 是指很难找出任意两个生成相同摘要(相同而非特定)的输入值。
可以参考“生日问题”,即在一个班级中,存在两个生日为同一天的学生的概率远比存在一个生日为特定日期的学生的概率大得多。

collision resistance 的意义在于,无法故意找出两套符合同一指纹的输入以达到混淆的目的。比如 MD5 算法:

>>> from hashlib import md5
>>> md5('bob').hexdigest()
'9f9d51bc70ef21ca5c14f307980a29d8'
>>> md5('cob').hexdigest()
'386685f06beecb9f35db2e22da429ec9'

对于很相似的输入 bob 和 cob,其指纹信息的差异却非常大,没有任何可供预测的规律。这得益于一种称为 avalanche property 的特性:输入的微小变化总可以在输出中产生巨大的无法预测的差异。

由前面提到的生日问题可知,找出两个生成相同指纹的元素远比找出某个可以生成特定指纹的元素要容易的多。以 MD5 算法的暴力破解为例,后者往往需要做 2^128 次尝试,而前者只需要 2^64 次尝试。
现实中 MD5 的 collision resistance 远非想象中那么优异,甚至存在一种非暴力破解的方式 能够在一小时以内攻破 MD5 的 collision resistance。
所以尽量不要使用 MD5 这个已经不再维护超过 10 年、安全漏洞存在 20 年的古老算法。

参考资料

Practical Cryptography in Python: Learning Correct Cryptography by Example

以上就是python密码学示例——理解哈希(Hash)算法的详细内容,更多关于python 哈希(Hash)算法的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
pygame播放音乐的方法
May 19 Python
星球大战与Python之间的那些事
Jan 07 Python
深入浅析python定时杀进程
Jun 06 Python
python+django快速实现文件上传
Oct 24 Python
用Python设计一个经典小游戏
May 15 Python
Python3实现发送QQ邮件功能(文本)
Dec 15 Python
python2.7到3.x迁移指南
Feb 01 Python
利用python GDAL库读写geotiff格式的遥感影像方法
Nov 29 Python
Django 实现Admin自动填充当前用户的示例代码
Nov 18 Python
pytorch 实现查看网络中的参数
Jan 06 Python
使用py-spy解决scrapy卡死的问题方法
Sep 29 Python
python实现Thrift服务端的方法
Apr 20 Python
python中的垃圾回收(GC)机制
Sep 21 #Python
如何在Python3中使用telnetlib模块连接网络设备
Sep 21 #Python
总结Pyinstaller的坑及终极解决方法(小结)
Sep 21 #Python
python生成xml时规定dtd实例方法
Sep 21 #Python
Python中的特殊方法以及应用详解
Sep 20 #Python
matplotlib 三维图表绘制方法简介
Sep 20 #Python
Python三维绘图之Matplotlib库的使用方法
Sep 20 #Python
You might like
封装ThinkPHP的一个文件上传方法实例
2014/10/31 PHP
php中通过DirectoryIterator删除整个目录的方法
2015/03/13 PHP
PHP判断FORM表单或URL参数来的数据是否为整数的方法
2016/03/25 PHP
PHP的静态方法与普通方法用法实例分析
2019/09/26 PHP
getElementsByTagName vs selectNodes效率 及兼容的selectNodes实现
2010/02/26 Javascript
jquery序列化form表单使用ajax提交后处理返回的json数据
2014/03/03 Javascript
js判断元素是否隐藏的方法
2014/06/09 Javascript
JQuery中DOM实现事件移除的方法
2015/06/13 Javascript
js实现a标签超链接提交form表单的方法
2015/06/24 Javascript
jQuery.trim() 函数及trim()用法详解
2015/10/26 Javascript
Vue2.0利用 v-model 实现组件props双向绑定的优美解决方案
2017/03/13 Javascript
jQuery鼠标悬停内容动画切换效果
2017/04/27 jQuery
原生JS实现多个小球碰撞反弹效果示例
2018/01/31 Javascript
JS继承实现方法及优缺点详解
2020/09/02 Javascript
解决vue-pdf查看pdf文件及打印乱码的问题
2020/11/04 Javascript
vue 使用饿了么UI仿写teambition的筛选功能
2021/03/01 Vue.js
Python通过DOM和SAX方式解析XML的应用实例分享
2015/11/16 Python
Python针对给定列表中元素进行翻转操作的方法分析
2018/04/27 Python
使用python中的in ,not in来检查元素是不是在列表中的方法
2018/07/06 Python
详解python while 函数及while和for的区别
2018/09/07 Python
Django利用cookie保存用户登录信息的简单实现方法
2019/05/27 Python
python 实现兔子生兔子示例
2019/11/21 Python
python3.8.1+selenium实现登录滑块验证功能
2020/05/22 Python
详解anaconda安装步骤
2020/11/23 Python
幼儿园长自我鉴定
2013/10/17 职场文书
令人啧啧称赞的经理推荐信
2013/11/07 职场文书
电子信息专业学生自荐信
2013/11/09 职场文书
清扬洗发水广告词
2014/03/14 职场文书
2014年小学重阳节活动策划方案
2014/09/16 职场文书
2015年依法治校工作总结
2015/07/27 职场文书
感恩老师主题班会
2015/08/12 职场文书
2019年聘任书的写作格式及范文!
2019/07/03 职场文书
PHP策略模式写法
2021/04/01 PHP
mysql 8.0.24版本安装配置方法图文教程
2021/05/12 MySQL
Oracle更换为MySQL遇到的问题及解决
2021/05/21 Oracle
nginx刷新页面出现404解决方案(亲测有效)
2022/03/18 Servers