Tech Ideas

C++,Linux,Algorithm,Crypto,Lisp,etc

现代密码学实践指南[2015年]


本文介绍目前现代密码学的最先进技术, 前半部分主要翻译自 《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模式有关:

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()

Comments