哈希 (Hash)

作者:之幽
链接:https://www.zhihu.com/question/26762707/answer/40119521
来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

---------------------------------------------------------

Q:一段字符串怎么hash? 一段长达1TB的二进制文件又如何hash? 他们都不是一个数字。

A:hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另一段固定长度的数据h,作为这段数据的特征(指纹)。

也就是说,无论数据块m有多大,其输出值h为固定长度。到底是什么原理?将m分成固定长度(如128位),依次进行hash运算,然后用不同的方法迭代即可(例如:前一块的hash值与后一块的hash值进行异或)。如果不够128位怎么办?用0补全或者用1补全随意,算法中约定好就可以了。

---------------------------------------------------------

由于用途的不同,hash在数据结构中的含义和密码学中的含义并不相同,所以在这两种不同的领域里,算法的设计侧重点也不同。

预备小知识:

抗碰撞能力:对于任意两个不同的数据块,其hash值相同的可能性极小;对于一个给定的数据块,找到和它hash值相同的数据块极为困难。

抗篡改能力:对于一个数据块,哪怕只改动其一个比特位,其hash值的改动也会非常大。


[情景1]

在用到hash进行管理的数据结构中,比如hashmap,hash值(key)存在的目的是加速键值对的查找key的作用是为了将元素适当地放在各个桶里,对于抗碰撞的要求没有那么高。换句话说,hash出来的key,只要保证value大致均匀的放在不同的桶里就可以了。但整个算法的set性能,直接与hash值产生的速度有关,所以这时候的hash值的产生速度就尤为重要,以JDK中的String.hashCode()方法为例:

    public int hashCode() {
        int h = hash;
 //hash default value : 0 
        if (h == 0 && value.length > 0) {
 //value : char storage
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

很简洁的一个乘加迭代运算,在不少的hash算法中,使用的是异或+加法进行迭代,速度和前者差不多。


[情景2]

在密码学中,hash算法的作用主要是用于消息摘要和签名,换句话说,它主要用于对整个消息的完整性进行校验。举个例子,我们登陆知乎的时候都需要输入密码,那么知乎如果明文保存这个密码,那么黑客就很容易窃取大家的密码来登陆,特别不安全。那么知乎就想到了一个方法,使用hash算法生成一个密码的签名,知乎后台只保存这个签名值。由于hash算法是不可逆的,那么黑客即便得到这个签名,也丝毫没有用处;而如果你在网站登陆界面上输入你的密码,那么知乎后台就会重新计算一下这个hash值,与网站中储存的原hash值进行比对,如果相同,证明你拥有这个账户的密码,那么就会允许你登陆。银行也是如此,银行是万万不敢保存用户密码的原文的,只会保存密码的hash值而而已。

在这些应用场景里,对于抗碰撞和抗篡改能力要求极高,对速度的要求在其次。一个设计良好的hash算法,其抗碰撞能力是很高的。以MD5为例,其输出长度为128位,设计预期碰撞概率为1/2^{64},这是一个极小极小的数字——而即便是在MD5被王小云教授破解之后,其碰撞概率上限也高达1/2^{41},也就是说,至少需要找2^{40}次才能有1/2的概率来找到一个与目标文件相同的hash值。而对于两个相似的字符串,MD5加密结果如下:

MD5("version1") = "966634ebf2fc135707d6753692bf4b1e";
MD5("version2") = "2e0e95285f08a07dea17e7ee111b21c8";

可以看到仅仅两个比特位(char("1")和char("2"))的改变,二者的MD5值就天差地别了。

到这里,读者估计会问,有没有可能找到这么一个算法,如果输出长度为128位,那么把这128位“充分利用到”,让它可以有2^{128}种不同的hash值,而且分布均匀,抗篡改能力也特别高,一点点改动就会让hash值面目全非,一点都不浪费(这里的表述非常不严格)?稍微严格一点表述,就是:有没有这样一个算法,使得对于任何一个给定的输入,此算法都会输出一个固定的均匀随机的输出?
答案是密码学家们也至今没有构造出着这样一个算法,但是倾向于这个算法存在,而且有不少的密码学算法构造和这个假设有关。这个假设的名字叫做随机预言机(Random Oracle)。

在密码学中,hash算法有不少有意思的改进思路,以应付不同的使用场景。例如师兄@刘巍然-学酥 前一段时间让我写着玩的变色龙Hash(ChameleonHash),它有一个有趣的特性。在普通情况下,ChameleonHash可以当做普通hash算法使用,从明文(用m表示)得到的hash值(用h表示)抗碰撞能力依然特别强;但是如果使用者在计算这个hash值的时候预先计算一个值(用s表示)并保存,那么通过这个值很容易计算出另一个hash值也为h的明文m' !也就是说,如果你保留这个值的话,hash算法的抗碰撞能力完全被解除了。

这意味着,如果某个网站想要作恶的话,那么它可以很容易的替换他们自己的hash算法为ChameleonHash,方便地伪造出一个密钥来窃取用户的所有数据,而这个公司完全可以在对外宣传的时候,依然声称对用户信息 严格保密——《教网站如何优雅地耍流氓》。







猜你喜欢

转载自blog.csdn.net/u011144776/article/details/80679087