0%

浅入浅出Hash算法

本文预计阅读需3min

你好,我是你老朋友Payne,大家都或许过我之前写的水文-JS解密入门,没看过的童鞋开源回头看看啊。里面主要讲述了Hash MD5的例子,以及加密与解密,相关的。那么今天我们去搞一下MD5的“父亲”, Hash。主要阐述了什么是哈希,哈希运用方向以及hash碰撞及解决方向,请查阅

Hash算法

哈希

(hash)也叫散列。Hash算法,是将一个不定长的输入,通过散列函数变换成一个定长的输出,即散列值。

应用

Hash主要应用在数据结构以及密码学领域。

数据结构:

使用Hash算法的数据结构叫做哈希表,也叫散列表,主要是为了提高查询的效率。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数就是hash function,存放记录的数组叫做哈希表。 在数据结构中应用时,有时需要较高的运算速度而弱化考虑抗碰撞性,可以使用自己构建的哈希函数。

密码学:

这种hash散列变换是一种单向运算,具有不可逆性即不能根据散列值还原出输入信息,因此严格意义上讲Hash算法是一种消息摘要算法,不是一种加密算法。常见的hash算法有:SM3、MD5、SHA-1等 。 在不同的应用场景下,hash函数的选择也会有所侧重。比如在管理数据结构时,主要要考虑运算的快速性,并且要保证hash均匀分布;而应用在密码学中就要优先考虑 抗碰撞性 ,避免出现两段不同明文hash值相同的情况发生。

哈希碰撞:

不同的值经过Hash function得到同一值则产生哈希碰撞防止哈希碰撞的最有效方法,就是扩大哈希值的取值空间

1
2
3
16个二进制位的哈希值,产生碰撞的可能性是 65536 分之一。如果有65537个用户,就一定会产生碰撞。哈希值的长度扩大到32个二进制位,碰撞的可能性就会下降到 4,294,967,296 分之一。

更长的哈希值意味着更大的存储空间、更多的计算,将影响性能和成本。开发者s必须做出抉择,在安全与成本之间找到平衡。

hash碰撞解决办法:

1. 开放地址法

  1. 线性探测再散列
  2. 二次探测再散列
  3. 伪随机探测再散列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入 。

    开放寻址法:Hi=(H(key) + di) MOD m,i=1,2,…,k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法:

    1). di=1,2,3,…,m-1,称线性探测再散列;

    2). di=1^2,(-1)^2,2^2,(-2)^2,(3)^2,…,±(k)^2,(k<=m/2)称二次探测再散列;

    3). di=伪随机数序列,称伪随机探测再散列。

    用开放定址法解决冲突的做法是:当冲突发生时,使用某种探测技术(线性探测法、二次探测法(解决线性探测的堆积问题)、随机探测法(和二次探测原理一致,不一样的是:二次探测以定值跳跃,而随机探测的散列地址跳跃长度是不定值))在散列表中形成一个探测序列。

    沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止插入即可。
  4. 再哈希法

再哈希法又叫双哈希法,有多个不同的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数去计算地址,直到无冲突。 虽然不易发生聚集,但是增加了计算时间。

  1. 链地址法(Java hashmap)

链地址法的基本思想是:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,将所有关键字为同义词的结点链接在同一个单链表中,

  1. 建立公共溢出区

建立公共溢出区的基本思想是:假设哈希函数的值域是 [1,m-1],则设向量 HashTable[0…m-1] 为基本表, 每个分量存放一个记录,另外设向量 OverTable[0…v] 为溢出表,所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。