网站首页 > java教程 正文
注:这篇文章源自我10年前写的博客,今天看到有人谈密码安全的,再发一遍和大家讨论下。我发现哪怕10年后,这文章也没过时,很多人还是没拎清 冲突概率和样本空间的关系。
前段时间跟某大牛叽歪的时候,被提到我写的一篇文章(用CRC32实现短网址的一篇)里提到的CRC32算法有误。今天写代码,恰好需要用到这个函数,想起来了,就又回去看了下。确认了下,原先的文章并没有错误,但是有一处描述是很有问题的。
原文是这样的,『综合以上的思路,决定采用CRC32来实现。CRC32也是一个哈希算法,和MD5类似,不过它是32位的,故更短一些,速度也更快。它所能表示的范围为40亿,也会产生冲突,但是对于一般应用足够了,这也是个成本很低廉的做法。』
这只是提到的一种简单做法而已。但是确实是在这里可能误导数学不好的读者。我们知道,CRC32是32位的,MD5是128位(谁说16位、32位, 我敲死他。这里的位是bit,不是字符长度),也可以说是CRC128的演化版。通常我们习惯用MD5来做hash。但是我在之前文章为了简单和压缩长度,使用了CRC32。容易知道,CRC32的值域是2^32,即大约40亿。
很多人想当然地理解为,CRC32的冲突概率是1/2^32,这是错的。
对样本空间的误解
这是不是说在实际应用中,我们就可以大胆使用CRC32了呢?因为一般来说,我们所用的数据很少过亿,更别说40亿了。在原文,我是作为短网址算法的引子展开的。就算腾讯那样的规模,也没那么容易超过40亿(腾讯的短网址算法是用在微博里的)。
如果你回答,应该是的,那你的高数是周公教的。在这里,我们说的40亿分之一的指的是CRC32所拥有的样本空间。在概率论中,尤其要注意至少这样的字眼。CRC32的样本空间是2^32次方,这并不表 明在40亿(2的32次方)的数据中就才可能产生一对碰撞,而是比这大得多。
假设有1000万组数据,
则第一个数据不碰撞的概率为,1/4000000000
第二个数据不和第一个碰撞的概率是(4000000000-1)/4000000000
以此类推,1000万个数据完全不碰撞的概率是个小的不能再小的数字,反过来,就是有可能碰撞了,即“完全不碰撞”的逆事件。1减去一个小的不能再小的数据,那么就是一个大的不能再大的数据了(和小于1的数比较)。想到了什么?生日悖论!不是吗?
生日悖论
也叫做"生日问题"(birthday problem):一个班级需要有多少人,才能保证每个同学的生日都不一样?
答案很出人意料。如果至少两个同学生日相同的概率不超过5%,那么这个班只能有7个人。事实上,一个23人的班级有50%的概率,至少两个同学生日相同;50人班级有97%的概率,70人的班级则是99.9%的概率。
这意味着,如果哈希值的取值空间是365,只要计算23个哈希值,就有50%的可能产生碰撞。也就是说,哈希碰撞的可能性,远比想象的高。
来推算一下:
至少两个人生日相同的概率,可以先算出所有人生日互不相同的概率,再用 1 减去这个概率。
我们把这个问题设想成,每个人排队依次进入一个房间。第一个进入房间的人,与房间里已有的人(0人),生日都不相同的概率是365/365;第二个进入房间的人,生日独一无二的概率是364/365;第三个人是363/365,以此类推。
因此,所有人的生日都不相同的概率,就是下面的公式。
上面公式的 n 表示进入房间的人数。可以看出,进入房间的人越多,生日互不相同的概率就越小。
这个公式可以推导成下面的形式。
那么,至少有两个人生日相同的概率,就是 1 减去上面的公式。
Hash冲突的概率
同理。
任意两个数经过CRC32后不碰撞,是一个随机事件。根据期望的线性限制,对模型简化,可以得到,碰撞对的数目,即E(X)=k(k-1)/2n,代入1000万,结果可知。实际上,只需要92682组数据,CRC32就已经有碰撞的可能性了。
那么为了让1000万组数据碰撞的可能性不大于1/2,也该有多少位呢?如果利用从一个全散列中随机选出的散列函数h,将n个关键字存储到一个大小为 m=n^2的那么出现碰撞的概率小于1/2.E(X)=(n,2)*1/n^2<1/2。即N的桶最好承载square(N)的数据,这样保证冲突概率小于50%。
到这里已经很清楚了,CRC32不是足够用,而是用不成的。
很轻松就能举出CRC32碰撞的例子,因为它的碰撞概率不到10W分之一,随便写个循环就可以跑出来。
我举个例子
package baicai.other;
import java.util.zip.CRC32;
public class Crc32 {
public static void main(String[] args) {
CRC32 crc32 = new CRC32();
crc32.update("86821".getBytes());
System.out.println(crc32.getValue());
CRC32 crcTwo = new CRC32();
crcTwo.update("14740600".getBytes());
System.out.println(crcTwo.getValue());
}
}
这个例子,两个数字就冲突了,它们的CRC32结果都是 350569284
那MD5呢,MD5冲突概率虽然小得多,但也是很容易发生的,也很容易构造。这里也给个例子
下面这张图片,它显示的内容也是它本身的MD5值,这明显是精心构造的碰撞
[ren@ren-pc 下载]$ md5sum md5.gif
f5ca4f935d44b85c431a8bf788c0eaca md5.gif
注:由于上传的关系,这种图片可能被压缩,会导致你验证失败。各位读者可以自行下载验证下。
猜你喜欢
- 2024-10-30 我的编程梦----聊聊Java特性(java编程特点的具体体现)
- 2024-10-30 java工程师SSH框架实战开发视频教程网盘下载
- 2024-10-30 教程:如何在Windows下快速搭建安卓开发环境
- 2024-10-30 ubuntu下安装JDK的详细步骤(ubuntu安装jdk教程)
- 2024-10-30 Java环境快速搭建(如何搭载java环境)
- 2024-10-30 2017Java面试技巧实战视频教程网盘下载
- 2024-10-30 阿里这份15w字Java核心面试笔记!GitHub凭借百万下载量位居榜首
- 2024-10-30 这里有正确使用SOLIDWORKS Composer帮助文档教程哦
- 2024-10-30 Java编程——搭建开发环境(java开发环境配置步骤)
- 2024-10-30 阿里P8熬了一个月肝出这份32W字Java面试手册,在Github标星31K+
你 发表评论:
欢迎- 07-15采用Oracle OSB总线进行服务注册和接入
- 07-15javaEE 新闻管理系统 oracle11+tomcat6
- 07-15从Oracle演进看数据库技术的发展(oracle数据库发展史)
- 07-15如何升级oracle数据库安全补丁(oraclepsu补丁升级)
- 07-15【权威发布】关于Oracle WebLogic Server未授权远程代码执行高危漏洞的预警通报
- 07-15【mykit-data】 数据库同步工具(数据库表同步工具)
- 07-15[Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)
- 07-15Google前工程主管“入住”Oracle(google浏览器找不到以前的书签)
- 最近发表
-
- 采用Oracle OSB总线进行服务注册和接入
- javaEE 新闻管理系统 oracle11+tomcat6
- 从Oracle演进看数据库技术的发展(oracle数据库发展史)
- 如何升级oracle数据库安全补丁(oraclepsu补丁升级)
- 【权威发布】关于Oracle WebLogic Server未授权远程代码执行高危漏洞的预警通报
- 【mykit-data】 数据库同步工具(数据库表同步工具)
- [Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)
- Google前工程主管“入住”Oracle(google浏览器找不到以前的书签)
- Oracle数据库云服务系列新增前所未有的企业级功能
- 直播预告丨如何实现Oracle存储过程到java的一键转化
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)