现代密码学实践指南[2015年]
下文分类介绍在各种适用场景下,你应该使用的现代密码学算法
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%E2%80%93Hellman_key_exchange 在任何一本密码学教材里面都会重点介绍的
13. constant time compare
针对Timing attack,http://en.wikipedia.org/wiki/Timing_attack (这种攻击真是脑洞大开!) 当一个算法的运行时间和输入数据有关的时候,可以根据运行时间这一信息,破解出密钥等。 典型的,比如要验证一个对称签名,如果你用了C库里面的memcmp(),那你就会被timing attack方式攻击。 因此,涉及到密码学数据的memcmp,必须要用运行时间和输入无关的函数,比如OpenSSL库里面的CRYPTO_memcmp()