首页 编程语言 php

你一定要知道的PHP的核心算法——hash算法解析

Hash概念

所谓Hash,是计算机算法领域内的一个概念,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值(哈希值)。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说Hash就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。如图1所示。

元素特征转变为数组下标的方法就是散列法。散列法当然不止一种,下面列出三种比较常用的:

  • 除法散列法

公式: index = value % 16

学过汇编的都知道,求模数其实是通过一个除法运算得到的,所以叫“除法散列法”。

  • 平方散列法

求index是非常频繁的操作,而乘法的运算要比除法来得省时(对现在的CPU来说,估计我们感觉不出来),所以我们考虑把除法换成乘法和一个位移操作。

公式: index = (value * value) >>28 右移,除以2^28。记法:左移变大,是乘。右移变小,是除。

如果数值分配比较均匀的话这种方法能得到不错的结果。也许你还有个问题,value如果很大,value* value不会溢出吗?答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,而是为了获取index。

Hash算法的特点

  • 正向快速:给定明文和 hash 算法,在有限时间和有限资源内能计算出 hash 值。

  • 逆向困难:给定(若干) hash 值,在有限时间内很难(基本不可能)逆推出明文。

  • 输入敏感:原始输入信息修改一点信息,产生的 hash 值看起来应该都有很大不同。

  • 冲突避免:很难找到两段内容不同的明文,使得它们的 hash 值一致(发生冲突)。即对于任意两个不同的数据块,其hash值相同的可能性极小;对于一个给定的数据块,找到和它hash值相同的数据块极为困难。

Hash算法的实现

作为散列算法,首要的功能就是要使用一种算法把原有的体积很大的文件信息用若干个字符来记录,还要保证每一个字节都会对最终结果产生影响。那么大家也许已经想到了,求模这种算法就能满足我们的需要。

事实上,求模算法作为一种不可逆的计算方法,已经成为了整个现代密码学的根基。只要是涉及到计算机安全和加密的领域,都会有模计算的身影。散列算法也并不例外,一种最原始的散列算法就是单纯地选择一个数进行模运算,比如以下程序。

很显然,上述的程序完成了一个散列算法所应当实现的初级目标:用较少的文本量代表很长的内容(求模之后的数字肯定小于8)。但也许你已经注意到了,单纯使用求模算法计算之后的结果带有明显的规律性,这种规律将导致算法将能难保证不可逆性。所以我们将使用另外一种手段,那就是异或。

再来看下面一段程序,我们在散列函数中加入一个异或过程。

很明显的,加入一层异或过程之后,计算之后的结果规律性就不是那么明显了。

当然,大家也许会觉得这样的算法依旧很不安全,如果用户使用连续变化的一系列文本与计算结果相比对,就很有可能找到算法所包含的规律。但是我们还有其他的办法。比如在进行计算之前对原始文本进行修改,或是加入额外的运算过程(如移位),比如以下程序。

这样处理得到的散列算法就很难发现其内部规律,也就是说,我们并不能很轻易地给出一个数,让它经过上述散列函数运算之后的结果等于4——除非我们去穷举测试。

Hash实例

  • 加法Hash

所谓的加法Hash就是把输入元素一个一个的加起来构成最后的结果。标准的加法Hash的构造如下:

staTIc int addiTIveHash(String key, int prime)

{

int hash, i;

for (hash = key.length(), i = 0; i 《 key.length(); i++)

hash += key.charAt(i);

return (hash % prime);

}

这里的prime是任意的质数,看得出,结果的值域为[0,prime-1]。

  • 位运算Hash

这类型Hash函数通过利用各种位运算(常见的是移位和异或)来充分的混合输入元素。比如,标准的旋转Hash的构造如下:

staTIc int rotaTIngHash(String key, int prime)

{

int hash, i;

for (hash=key.length(), i=0; i

hash = (hash《《4》》28)^key.charAt(i);

return (hash % prime);

}

先移位,然后再进行各种位运算是这种类型Hash函数的主要特点。比如,以上的那段计算hash的代码还可以有如下几种变形:

hash = (hash《《5》》27)^key.charAt(i);

hash += key.charAt(i);

hash += (hash 《《 10);

hash ^= (hash 》》 6);

if((i&1) == 0)

{

hash ^= (hash《《7》》3);

}

else

{

hash ^= ~((hash《《11》》5));

}

hash += (hash《《5》

hash = key.charAt(i) + (hash《《6》》16) ? hash;

hash ^= ((hash《《5》》2));

  • 信息安全

在信息安全领域中应用的Hash算法,还需要满足其他关键特性:

  1. 单向性(one-way),从预映射,能够简单迅速的得到散列值,而在计算上不可能构造一个预映射,使其散列结果等于某个特定的散列值,即构造相应的M=H-1(h)不可行。这样,散列值就能在统计上唯一的表征输入值,因此,密码学上的 Hash 又被称为"消息摘要(message digest)",就是要求能方便的将"消息"进行"摘要",但在"摘要"中无法得到比"摘要"本身更多的关于"消息"的信息。

  2. 抗冲突性(collision-resistant),即在统计上无法产生2个散列值相同的预映射。给定M,计算上无法找到M',满足H(M)=H(M') ,此谓弱抗冲突性;计算上也难以寻找一对任意的M和M',使满足H(M)=H(M') ,此谓强抗冲突性。要求"强抗冲突性"主要是为了防范所谓"生日攻击(birthday attack)",在一个10人的团体中,你能找到和你生日相同的人的概率是2.4%,而在同一团体中,有2人生日相同的概率是11.7%。类似的,当预映射的空间很大的情况下,算法必须有足够的强度来保证不能轻易找到"相同生日"的人。

  3. 映射分布均匀性和差分分布均匀性,散列结果中,为 0 的 bit 和为 1 的 bit ,其总数应该大致相等;输入中一个 bit 的变化,散列结果中将有一半以上的 bit 改变,这又叫做"雪崩效应(avalanche effect)";要实现使散列结果中出现 1bit 的变化,则输入中至少有一半以上的 bit 必须发生变化。其实质是必须使输入中每一个 bit 的信息,尽量均匀的反映到输出的每一个 bit 上去;输出中的每一个 bit,都是输入中尽可能多 bit 的信息一起作用的结果。

相关推荐