单向散列函数的两个特点:
- 逆向运算困难
- 构造碰撞困难
这两个特点使得我们仅仅修改输入数据中的一位,所得到的散列值都会有很大的差异,所以说,这两个特点也决定了一个单向散列函数的破解难度,逆向运算越难,破解难度越难;构造碰撞越难,破解难度也越难
一、困难程度
我们要探讨的第一个问题就是,一个单向散列函数的逆向运算和构造碰撞要困难到什么程度,它才能算是一个合格的单向散列函数?
“完美”的单向散列函数,应该困难到没有人可以逆向运算,也没有人可以构造碰撞,一旦有人找到一对具有相同散列值的数据,不管这对数据有多么的千奇百怪,更不管破解方式是多么的不合常理,结果就意味着这个单向散列函数被破解了,不再安全了
比如说,下面的两段不同数据有着相同的 MD5 散列值(MD5 是一个单向散列函数)
这也就意味着 MD5 算法被破解了,不再安全了,不能够再继续使用了
data1:
4dc968ff 0ee35c20 9572d477 7b721587 d36fa7b2 1bdc56b7 4a3dc078 3e7b9518
afbfa202
a8284bf3 6e8e4b55 b35f4275 93d84967
6da0d1d5
5d8360fb 5f07fea2
data2:
4dc968ff 0ee35c20 9572d477 7b721587 d36fa7b2 1bdc56b7 4a3dc078 3e7b9518
afbfa200
a8284bf3 6e8e4b55 b35f4275 93d84967
6da0d155
5d8360fb 5f07fea2
Hash: MD5(data1) = MD5(data2)
008ee33a 9d58b51c feb425b0 959121c
二、安全强度(Security Strength)
1、什么是安全强度
有没有一个直观的指标,能够让我们感受下那些还没有被破解的算法到底是有多安全?那些已经被破解的算法是有多脆弱?
在密码学这么讲究量化的领域,当然不会缺少这样的指标,其中,最常用的就是安全强度,通常用“位”表示,比如说,安全强度是 32 位,破解一个 N 位安全强度的算法需要 2^N(2 的 N 次方) 次运算
MD5,它的安全强度最多 18 位,也就是说,我们运算 2^18=262144 次就可以破解,按现在计算机一毫秒一次的运算速度来看,262144 毫秒,也就是 4 分多钟就能够破解,没错,MD5 在现在看来就是这么弱
那 128 位的安全强度呢?假设我们现在有一台速度快 1000 倍的计算机,它能做到 1 纳秒运算一次,再做类似上面的运算,最终结果是 1.07902832e+22 年
到这儿,相信你通过安全强度可以直观感受到那些还没有被破解的算法到底是有多安全,而那些已经被破解的算法是有多脆弱,所以,在实际应用中,我们应该优先选择那些安全强度足够高的算法
2、安全强度是否会变
每一个密码学算法在诞生的时候,都会有一个理论上的设计安全强度,注意,理论上的意思就是有可能与实际情况不符,比如单向散列函数 SHA-1 在 1993 年发布的时候,它的设计安全强度是 80 位,但是 12 年后的 2005 年 2 月,中国密码学专家王小云教授带领的研究团队发现,SHA-1 的安全强度小于 69 位,远小于设计之初的 80 位,从此,SHA-1 的安全强度开始一路衰减,很快,在 2005 年 8 月,王小云教授的团队又改进了破解算法,发现 SHA-1 的安全强度只有 63 位了
2015 年 10 月,密码学专家马克·史蒂文斯(Marc Stevens)、皮埃尔·卡普曼(Pierre Karpman)和托马斯·佩林(Thomas Peyrin)的研究团队发现 SHA-1 的安全强度只有 57.5 位,更要紧的是,他们估算,如果使用云计算,按照 2015 年亚马逊 EC2 云计算的算力和定价,57 位安全强度的算法,破解成本大约是 10 万美元,这在当时,无论是对于有组织的研究机构,还是犯罪集团,都是一个很小的数目
通过 SHA-1 的例子,我想强调的就是,一个算法的安全强度不是一成不变的。随着安全分析的进步,几乎所有密码学算法的安全强度都会衰减,今天看起来安全的算法,明天也许就有破解的办法
3、使用多大的安全强度
业界内最新推荐的三个常用指标:
- 美国的 NIST(国家标准技术研究所)
- 德国的 BSI(联邦信息安全办公室)
- 欧洲的 ECRYPT-CSA(欧洲卓越密码网络)
安全强度(位) | NIST 建议 | BSI 建议 | ECRYPT-CSA 建议 |
---|---|---|---|
80 | 仅遗留系统可以使用,不建议用于新系统 | 不推荐 | 仅遗留系统可以使用,不建议用于新系统 |
112 | 仅可用于 2030 年之前 | 不推荐 | 不推荐 |
128 | 可以用于 2030 年之后 | 可以用于 2020 年之后 | 可以用于 2028 年之前 |
256 | 可以用于 2030 年之后 | 可以用于 2020 年之后 | 可以用于 2068 年之前 |
ECRYPT-CSA 的建议为什么这么保守?其实,这种保守的背后,隐含了对量子计算时代来临的担忧,在量子计算时代,128 位的安全强度稍显脆弱,256 位的安全强度还是足够的
目前看来,128 位的安全强度是足够的,不过一个需要长期运营的系统,如果性能瓶颈不是问题,现在就可以开始考虑使用 256 位的安全强度了
三、可用的单向散列函数-SHA256/SHA384/SHA512
单向散列函数 | 安全强度(位) | 现在能否使用 | 发布日期 | 散列值长度(位) | 数据分块(位) | 处理能力(位) | 计算性能(CPB) |
---|---|---|---|---|---|---|---|
MD2 | - | 退役 | 1989.08 | 128 | 128 | - | - |
MD5 | ≤18 | 退役 | 1992.04 | 128 | 512 | 2^64 | 5.13 |
SHA-0 | <34 | 退役 | 1992.01 | 160 | 512 | 2^64 | - |
SHA-1 | <58 | 遗留 | 1995.04 | 160 | 512 | 2^64 | 1.96 |
SHA-2 | |||||||
SHA-224 | 112 | 遗留 | 2004.02 | 224 | 512 | 2^64 | 2.09 |
SHA-256 | 128 | 现行 | 2002.08 | 256 | 512 | 2^64 | 2.06 |
SHA-384 | 192 | 现行 | 2002.08 | 384 | 1024 | 2^128 | 4.86 |
SHA-512 | 256 | 现行 | 2002.08 | 512 | 1024 | 2^128 | 4.85 |
SHA-512/224 | 112 | 现行 | 2012.03 | 224 | 1024 | 2^128 | 7.15 |
SHA-512/256 | 128 | 现行 | 2012.03 | 256 | 1024 | 2^128 | 7.15 |
SHA-3 | |||||||
SHA3-224 | 112 | 遗留 | 2015.08 | 224 | 1152 | - | 6.46 |
SHA3-256 | 128 | 现行 | 2015.08 | 256 | 1088 | - | 6.96 |
SHA3-384 | 192 | 现行 | 2015.08 | 384 | 832 | - | 10.18 |
SHA3-512 | 256 | 现行 | 2015.08 | 512 | 576 | - | 12.35 |
SHAKE128 | min(d/2,128) | - | 2015.08 | d(任意长度) | 1344 | - | 5.11 |
SHAKE256 | min(d/2,256) | - | 2015.08 | d(任意长度) | 1088 | - | 6.95 |
1、可用的算法
判断一个算法现在能否使用是我们选择算法的第一步,根据这个标准,我把常见的算法归为以下三类:
- 退役的算法
- 遗留的算法
- 现行的算法
退役的算法,就是那些已经退出历史舞台的算法,它们的安全强度很弱,你一定不能再用了,如果你看到退役的算法还在使用,往往意味着这是一个过时的系统,或者说它的开发者缺少密码学常识(这也怪不得它的开发者,毕竟密码学常识一直没有得到普及),如果是我们自己能够掌控的系统,一定要尽快升级算法
遗留的算法?你只要记住,它们都存在明显的安全问题,已经不足以支撑现在的安全强度需求了,因为,遗留的算法,已经走在了退役的路上
那为什么还有遗留的算法?因为要留出一定的时间,给现有继续运营的系统进行算法升级
到了最后,只有现行的算法,才是我们现在可以选择的算法,这是因为其安全性,是经过很多密码学专家分析验证过的,一般到目前为止,还没有人能发现明显的安全缺陷
2、算法的处理能力
处理能力指的是单向散列函数能够处理的最大数据长度,比如,SHA-256 能够处理的最大数据长度是 2^64 位
3、算法的性能
表格中的计算性能参考的是 ECRYPT 在 2020 年 7 月和 2019 年 10 月对 4096 字节数据的性能基准测试结果,计算性能表示在数据运算时,处理一个字节数据需要执行的微处理器时钟周期数,它使用的度量单位是每字节周期数(CPB,Cycles Per Byte),CPB 是一个常用的密码算法实际性能参考指标,CPB 越小,表示这个算法运算得越快,性能也就越好
从理论上讲,一个算法的性能主要是由算法的复杂度决定的,这里有一个假设,就是不考虑其他因素的影响,但是在实践中,其他因素有时候才是影响算法性能的主要因素,比如实现细节
在一个算法的实现细节中,通常影响计算性能的因素有:
- 算法实现的内存使用影响
- 算法实现有没有使用优化的步骤,比如并行计算或者预运算?
- 算法实现有没有使用硬件加速,比如使用 CPU 关于算法的扩展指令?
一个规规矩矩的算法实现,它的性能一般落后于 CPU 扩展指令数十倍,遗憾的是,并不是每一个算法都有 CPU 扩展指令,或者每一个实现都支持 CPU 扩展指令
另外,计算机本身的指令集,比如是使用 32 位还是 64 位的指令,是否和算法匹配,也是影响算法性能的一个重要因素,我们经常可以看到,SHA-512 的计算速度比 SHA-256 还要快,SHA-256 使用 32 位的数据进行计算,而 SHA-512 使用 64 位的数据进行计算,现在的计算机,一般都是 64 位的,所以运行在 64 位的计算机上,基于 32 位的计算可能反而比基于 64 位的计算还要慢