本文介绍目前现代密码学的最先进技术, 前半部分主要翻译自 《Cryptographic Right Answers》,附上收集的资料,和byron个人的理解。
密码学理论艰深,概念繁多,本人知识水平有限,错误难免,如果您发现错误,请务必指出,非常感谢!
下文分类介绍在各种适用场景下,你应该使用的现代密码学算法
1. 加密数据 :
按照优先级,应该选择:
(1) 首选 NaCl库,或者libsodium库,使用里面的crypto_secretbox()/crypto_secretbox_open() 函数 (2) Chacha20-Poly1305 算法 (3) AES-GCM 算法
适用场景:当你需要避免把明文数据在网络上传输的时候。
以上3种算法,都是AEAD类的算法,AEAD是2015年最好的选择。 其中的(2)和(3)在结构上类似:一个流加密模式的算法,配合一个多项式结构的MAC。 (2)是一个流加密算法,配合一个为通用cpu优化的MAC算法, 对密码学库的实现者来说,Poly1305也比GCM更容易安全地实现。 AES-GCM是工业标准(TLS目前主要用的就是AES-GCM),现代CPU通常都有专门为AES-GCM设计的硬件指令,但是在没有硬件指令支持的CPU上(比如32位的arm),(3)性能低于(2)。
此外,应该
- 避免AES-CBC(说来话长,后文有解释)
- 避免AES-CTR
- 避免64bit块大小的块加密算法–(说的就是你–BlowFish)
- 避免OFB模式
- 不要使用RC4,RC4已经被攻破
2. 对称密钥长度 :
选择使用256bit长度的密钥
适用场景:只要你在使用密码学,你就应该注意对称密钥长度
请记住:不要把对称加密(如AES)的key长度,和非对称加密(如RSA)的key长度搞混淆了,对称加密的key通常比非对称加密的key短多了。
下表对比了相同安全程度时,不同算法的密钥长度,单位:bit
Symmetric | ECC | DH/DSA/RSA |
---|---|---|
80 | 163 | 1024 |
112 | 233 | 2048 |
128 | 283 | 3072 |
192 | 409 | 7680 |
256 | 571 | 15360 |
此外,应该
- 避免使用巨大key的算法(使用远大于256的key,只能说明使用者没有安全概念)
- 避免把多个加密算法串联叠加起来使用,这并没有什么卵用
- 避免128bit以下的key长度(比如,哥们求你别再提DES这种56bit密钥的古董了)
3. 对称签名:
应该选择 HMAC 类的算法
适用场景:安全加固一个API,如各种开放API的调用方认证
如果对一个API,你需要做认证(authenticating),但是不需要做加密(encrypting),记得千万不要自己发明算法,你自己发明的MAC算法基本都有安全漏洞,如果不信,请Google一下 “长度扩展攻击” 长度扩展攻击 Flickr的漏洞案例
同时,必须要注意的是,要使用一个常数时间字符串对比算法(这个地方和码农的常识完全相反,请务必留意)
此外,应该
- 避免自行设计的“带密码的hash”结构,你的设计基本都是有安全漏洞的
- 避免HMAC-MD5,避免HMAC-SHA1,使用HMAC-SHA256, HMAC-SHA512等
- 避免复杂的多项式MAC
- 避免加密hash值的结构
- 避免CRC
4. Hashing/HMAC 算法
应该选择SHA2类的算法:: SHA-256, SHA-384, SHA-512, SHA-512/256
优先使用 SHA-512/256,SHA-512/256这个算法把 SHA-512 的512bit输出截短到256bit,避开了length extension 攻击。 同时,目前SHA-2是很安全可靠的,你不需要升级到SHA-3.
此外,应该
- 避免SHA-1
- 避免MD5
- 避免MD6
5. 随机ID
应该使用256 bit的随机值
一定要使用 /dev/urandom,请认准这个
此外,应该
- 避免用户空间的随机数生成器如:havaged,prngs,egd,等
- 避免/dev/random
6. 密码处理
按照优先级顺序,选择:
- scrypt
- bcrypt
- 如果以上2个都没有,那就用PBKDF2
此外,应该
- 避免直接SHA-2
- 避免直接SHA-1
- 避免直接MD5
7. 非对称加密
应该使用NaCl库
适用场景:当你需要加密消息,发给陌生人,并且对方异步接收消息,做离线解密时。这是一个很窄的应用案例,这种用法有个名字叫电子信封(digital envelope),典型比如gpg加密文件后发送。
这条是几条之中最难做正确的,不要使用底层的密码学库,比如OpenSSL或者BouncyCastle。
你应该停止使用RSA,并且切换到椭圆曲线类体制,原因是:
- 对RSA的攻击能力的进步 — 定义在传统质数域上的乘法运算(应用包括DH,DSA,ElGamal等),要比椭圆曲线域上的乘法运算快得多。这是由于质数域上数域筛法(number field sieve,NFS)的进展,而在椭圆曲线域上,没有NFS这类算法。
- RSA (和DH) 或迫使你考虑“向后兼容性”,而椭圆曲线体制没有这种兼容性包袱。TLS最近的几个安全漏洞,部分愿意也是由于这种向后兼容性,导致已经被破解的陈旧算法存在
- RSA在一般场景中,都是直接用公钥做非对称加密,这种用法丧失了前向安全性(Perfect Forward Secrecy)。而椭圆曲线就不提倡,也很难这样使用,这样你就不会害死自己了。
- 在椭圆曲线体制下,保证正确性和安全性的重任,主要由密码学家承担,密码学家会提供一组曲线参数,在某一性能水平下,针对安全性和性能做优化。这样程序员不容易误用而害死自己。在RSA体制下,正好相反,程序员必须提供参数来保证正确性和安全性,就算是RSA-OAEP这种很好的设计,程序员也必须知道怎么提供参数,这样程序员很容易搞错。
如果你必须使用RSA,一定要使用RSA-OAEP with SHA256,指数使用 65537
- 避免 RSA-PKCS1v15
- 避免 ElGamal
- 避免 RSA
8. 非对称签名
应该使用NaCl,Ed25519,或者RFC6979
应用场景:如果你在设计一种新的比特币,或者一个给Ruby Gems或者Vagrant imges文件签名的系统,或者数字版权保护系统(DRM),其中一系列的文件需要离线做认证; 或者你在设计一个加密消息传输层
上一条的内容在此处全部适用。
在10 年做付费软件安全评估的工作经历中,我只有屈指可数的几次,遇到使用RSA-PSS的用户,RSA-PSS是一个学术界的推荐算法。
过去10年,非对称签名最主要的应用场景是比特币,和前向安全的密钥协商(TLS协议里面的ECDHE)。 其中最主要的算法全都是基于椭圆曲线体制的。务必警惕新出现的使用RSA签名的系统,很有可能有问题。
在过去几年中,业界有一种趋势:放弃传统DSA签名,改为难以误用的确定性签名体制,其中的EdDSA(不要和ECDSA搞混了喂!)和RFC6979是最好的例子。这种趋势的主要是受到2010年索尼PlayStation 3的 ECDSA私钥被破解事件的影响,在这个案例中,索尼公司的码农错误地把一个随机数重复使用来做ECDSA签名,形成了漏洞,使得破解者据此直接把私钥算出来了。确定性签名体制在设计中不再依赖随机数生成器,因此彻底避开此类误用。所以你应该优先使用确定性签名体制。
- 避免RSA-PKCS1v15,避免RSA,避免ECDSA,避免DSA
- 特别要避免常规的DSA和ECDSA
9. Diffie-Hellman 密钥交换
应该使用NaCl,Curve25519,或者DH-2048
适用场景:如果你在设计加密消息传输系统,并且无法使用固定对称密码
这是很棘手的一条,主要考量如下:
- 如果你能使用NaCl库,那就使用NaCl库。你甚至不需要管NaCl是什么。
- 如果你能使用一个可信赖的第三方库,那就使用Curve25519,这是一条现代的ECDH曲线,有丰富的开源代码,性能经过高度优化,被彻底地安全分析过。并且Curve25519即将进入TLS 1.3版本标准。
- 但是绝对不要自己实现Curve25519,也绝对不要自己移植Curve25519的C代码
- 如果你不能使用第三方ECDH库,但是可以使用DH库,那就使用DH-2048,使用1个标准的2048 bit的群。
- 但是不要使用传统的DH,如果你需要协商DH参数,或者和其他实现互操作
- 如果你一定要做握手协商,或者和旧软件互操作,那么考虑使用NIST P-256, NIST P-256 有广泛的软件支持。
- 写死在代码里的DH-2048参数,比NIST P-256更安全。NIST P-256比协商出来的DH更安全。
- 但是,由于NIST P-256的实现有一些陷阱,所以一定要谨慎选择可信赖的,广泛使用使的第三方库
- P-256 可能是NIST曲线中最安全的,不要使用P-224。
DH(密钥协商)算法确实很难用,但是它很重要。
- 避免,传统常规的 DH, SRP, J-PAKE 握手和协商
- 避开任何只使用了块加密算法和srand(time())的密钥协商模式(肯定有漏洞)
10. 网站安全
应该使用OpenSSL,或者Google的BoringSSL,或者直接使用 AWS的 ELB
此处网站安全,指的是让网站支持HTTPS协议。 如果你不能把这个任务交给Amazon的云服务去做,把难题留给Amazon去解决,那么OpenSSL目前仍然是正确选择。
- 避免不常见的TLS库,例如polarssl,GnuTLS,MatrixSSL等
11. 客户端-服务器结构的应用程序的安全:
应该使用TLS
适用场景:如果你以为自己理解了前面关于公钥加密的介绍。。。
通常,在你设计了自己的RSA协议之后的1至18个月,你肯定会发现,你犯了某个错误,使你的协议没有任何安全性。 比如Salt Stack,Salt Stack的协议使用了 e=1 的RSA 公钥。。。
听起来,TLS有下面这些黑历史:
- The Logjam DH negotiation attack
- The FREAK export cipher attack
- The POODLE CBC oracle attack
- The RC4 fiasco
- The CRIME compression attack
- The Lucky13 CBC padding oracle timing attack
- The BEAST CBC chained IV attack
- Heartbleed
- Renegotiation
- Triple Handshakes
- Compromised CAs
但是,你仍然应该使用TLS做传输协议,因为:
- 这些漏洞中的大部分,仅仅是针对浏览器的,因为他们依赖受害者执行攻击者控制的JavaScript脚本,这些JavaScript脚本生成重复的明文,或特定的明文。
- 这些漏洞中的大部分,其影响都可以被减轻,只需要你在代码和配置里面写死 TLS v1.2, ECDHE,和 AES-GCM就行。这听起来很棘手,但是这远远没有你自己设计使用ECDHE和AES-GCM的传输协议棘手。
- 在一个自定义的传输协议的场景中,你并不需要依赖CA,你可以用一个自签名证书,嵌入到你的客户端里面。
- 不要自己设计加密传输协议,这是极其困难而易错的工程难题
- 使用TLS,但是不要使用默认配置
12. 在线备份
应该使用Tarsnap
#名词解释
本文的内容比较新,相关中文资料极少,因此文中的名词对读者可能有点陌生,故byron这里介绍一下文中提到的一些名词:
1. NaCl库:
http://nacl.cr.yp.to/ 是密码学学术权威 Daniel J. Bernstein教授 设计的一个密码学算法库,2008年发开始公布。NaCl的特点是:api简洁而易用,高性能,高安全性,主要用于网络通信,加密,解密,签名等,NaCl提供了构建高层密码学工具的核心功能。
2. libsodium库:
https://download.libsodium.org/doc/ libsodium是对NaCl库的一个分支,进一步改进接口易用性,和可移植性。
3. AEAD:
https://www.imperialviolet.org/2014/02/27/tlssymmetriccrypto.html AEAD的概念: 在通常的密码学应用中,Confidentiality (保密) 用加密实现,Message authentication (消息认证) 用MAC实现。这两种算法的配合方式,引发了很多安全漏洞,过去曾经有3种方法:1. Encrypt-and-MAC 2.MAC-then-Encrypt 3.Encrypt-then-MAC ,后来发现,1和2都是有安全问题的,所以,2008年起, 逐渐提出了“用一个算法在内部同时实现cipher MAC”的idea,称为AEAD(Authenticated encryption with additional data)。 在AEAD这种概念里,cipher MAC 被 一个AEAD算法替换。 http://en.wikipedia.org/wiki/Authenticated_encryption
4. ChaCha20-poly1305
ChaCha20-poly1305是一种AEAD,提出者是Daniel J. Bernstein教授,针对移动互联网优化,目前Google对移动客户端的所有流量都使用ChaCha20-Poly1305
5. AES-GCM
AES-GCM是一种AEAD,是目前TLS的主力算法,互联网上https流量的大部分依赖使用AES-GCM。
6. AES-GCM和ChaCha20-Poly1305的性能对比测试结果:
Chip | AES-128-GCM speed | ChaCha20-Poly1305 speed |
---|---|---|
OMAP 4460 | 24.1 MB/s | 75.3 MB/s |
Snapdragon S4 Pro | 41.5 MB/s | 130.9 MB/s |
Sandy Bridge Xeon (AESNI) | 900 MB/s | 500 MB/s |
7. AES-CBC
关于AES-CBC,在AES-GCM流行之前,TLS主要依赖AES-CBC,而由于历史原因,TLS在设计之初固定选择了MAC-then-Encrypt结构,AES-CBC和MAC-then-encrypt结合,为选择密文攻击(CCA)创造了便利条件,TLS历史上有多个漏洞都和CBC模式有关:
- The POODLE CBC oracle attack:参考: 1.POODLE的一个分析 2.openssl的分析 3.乌云的文章
- The CRIME compression attack:
- The Lucky13 CBC padding oracle timing attack:
- The BEAST CBC chained IV attack:
8. SHA2
http://en.wikipedia.org/wiki/SHA-2
9. Curve25519
http://cr.yp.to/ecdh.html Curve25519 是目前最高水平的 Diffie-Hellman函数,适用于广泛的场景,由Daniel J. Bernstein教授设计。由于NIST P-256的设计过程不透明,有来历不明的参数,被广泛怀疑有后门,所以设计了Curve25519,Curve25519的设计过程完全公开,没有任何来历不明的参数。 部署情况:http://ianix.com/pub/curve25519-deployment.html
10. Ed25519
http://ed25519.cr.yp.to/ Ed25519是一个数字签名算法,
- 签名和验证的性能都极高, 一个4核2.4GHz 的 Westmere cpu,每秒可以验证 71000 个签名
- 安全性极高,等价于RSA约3000-bit
- 签名过程不依赖随机数生成器,不依赖hash函数的防碰撞性,没有时间通道攻击的问题
- 并且签名很小,只有64字节,公钥也很小,只有32字节。 部署情况:http://ianix.com/pub/ed25519-deployment.html
11. 前向安全性
前向安全性( Perfect Forward Secrecy ) http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html 前向安全性指的是,如果攻击者抓取并保存流量,那么将来私钥泄露后,攻击者也无法利用泄露的私钥解密这些流量。
12. Diffie-Hellman 密钥交换
http://en.wikipedia.org/wiki/Diffie–Hellman_key_exchange 在任何一本密码学教材里面都会重点介绍的
13. constant time compare
针对Timing attack,http://en.wikipedia.org/wiki/Timing_attack (这种攻击真是脑洞大开!) 当一个算法的运行时间和输入数据有关的时候,可以根据运行时间这一信息,破解出密钥等。 典型的,比如要验证一个对称签名,如果你用了C库里面的memcmp(),那你就会被timing attack方式攻击。 因此,涉及到密码学数据的memcmp,必须要用运行时间和输入无关的函数,比如OpenSSL库里面的CRYPTO_memcmp()