一些简单的密码学常用套路

序言


本文主要介绍一些加密库中加密算法的使用原则和需要注意的问题。
首要原则:如果工程中涉及任何安全加密相关的内容,绝对绝对不要尝试自己实现加密算法。

对称加密相关


  1. 当你不知道选什么加密算法时,用AES,配合不同的工作模式,它既可以用于分组加密,也可以用于流加密,保证安全性的同时在大多数平台上都有不俗的性能表现。

  2. 128位的AES已经足够安全,256位的AES现阶段并非必要,除非你想用它来抵御未来量子计算机的穷举攻击。

  3. AES不仅比DES(包括3DES)安全,还比DES快,除非已经有专用的DES加速硬件,否则没有选择DES的理由。

  4. 流加密算法用ChaCha20(或Salsa20),不要考虑RC4,早已被破解,而且ChaCha20不仅更安全,也快得多。

  5. 在有AES-NI指令集的平台上,使用AES速度会比较快,在没有该指令集的平台上,使用ChaCha20更快,而多数PC端CPU都有该指令集,多数移动端CPU没有该指令集(或者有类似指令集,但是效果也没有AES-NI好),因此有PC端用AES,移动端用ChaCha20的做法。

  6. 分组密码工作模式有ECB、CBC、CTR、CFB、OFB等,其中CTR、CFB、OFB支持流密码。

  7. ECB模式下同样的明文产生同样的密文,不安全,除非每一个加密块都换一次密码,不然不要选择这个模式。

  8. CBC的IV(初始化向量)必须是不可预测的随机数。

  9. CTR模式下,nonce只要求不可重复,一个常用的实践是,将一个不重复的64位的随机数跟一个64位的计数器拼接在一起,形成128位的nonce。

  10. CBC模式加密不能并行,解密可以并行。CTR模式各个密文块相互独立,加密解密都可以并行化。

  11. 只有AEAD算法(认证加密)才可以防篡改,比如AES-GCM、AES-OCB、ChaCha20-Poly1305等。打开一个HTTPS网站,它使用的加密方法多半是AES-GCM。部分网站在移动端上可能会采用的ChaCha20-Poly1305,它针对ARM架构的CPU进行了优化。

  12. 磁盘加密请用XTS-AES。

散列相关


  1. MD5、SHA-1已经相继被发现可以被恶意碰撞,比如谷歌造出来了两个内容不同但是SHA-1值相同的PDF文件,不要在工程中使用这两种散列算法,至少要使用SHA-256。

  2. 如果需要使用用户输入密码来加密信息,不要直接使用密码或者简单地做一下散列运算作为密钥,应该用专门的PBKDF2算法来导出密钥。

  3. 保存密码时,一个很常见的误区是对密码进行多次的MD5或者SHA-1计算,以为这样更加安全,实际上这种做法的安全性并未得到验证,反而可能会因为减小了摘要空间而降低安全性。

  4. 为了抵御彩虹表攻击,保存密码一定要加盐(salt),而且对每一个密码使用不同的salt。很多平台都会提供这样的API,这些API通常会把salt跟散列值作为整体输出,不需要修改数据库来另外保存salt。

  5. 需要注意的是,MD/SHA系列(比如MD5、SHA-256等等)本身并不是设计来保存密码的,所以即使SHA-256仍是安全的,裸的随机salt+SHA-256也不是好的选择。

  6. 保存密码推荐使用Bcrypt,它就是为密码储存专门设计的,在C, C#, Go, Java, JavaScript, Perl, PHP, Python, Ruby等众多语言中都有相应的实现。当然,更新的Scrypt和Argon2理论上安全性会更高,但是它们支持不够广泛,在很多平台上并不可用。

  7. 比对用户输入的密码跟数据库中保存的密码不能用语言中的==比较操作符或者strcmp函数,可能会被时序攻击,请使用固定运行时间的比较方法,例如PHP中提供的password_verify()函数。

非对称加密相关


  1. 非对称加密非常非常非常慢,比对称加密慢几个数量级。

  2. 所以一般使用非对称加密算法加密对称密钥,然后使用对称密钥加密消息内容。

  3. 通过Diffie–Hellman密钥交换得到密钥不能直接用,其分布并不均匀,为了安全,需要使用专门的密钥导出函数(Key Derivation Function)导出密钥。

  4. 椭圆曲线加密密钥比RSA短得多,要达到128位AES的加密强度,椭圆曲线密钥只需要256位,而RSA需要3072位。

  5. 但是椭圆曲线加密实现更复杂更容易出错。

  6. RSA中,公共指数e一般选择65537。

  7. RSA公钥长度至少选择2048位,1024位已经不再安全。

  8. 椭圆曲线加密时曲线建议选取Curve25519(包括用于密钥交换的X25519和用于数字签名的Ed25519),效率与安全性俱佳。如果Curve25519不可用(比如低版本的OpenSSL),建议选择prime256v1(又称P-256),支持广泛。

常用加密库比较


仅比较本人使用过的加密库,可能带有一定的主观倾向,仅供参考。

OpenSSL

使用C语言编写,是最老牌、使用最广泛的加密库之一,被集成于大多数Linux发行版中,大量网站依赖其提供HTTPS服务。

优点:使用极为广泛,效率和安全性久经考验,即使出现漏洞也可以很快被发现并修补,在不知道用什么的情况下是最稳妥的选择。

缺点:官方文档不够完善,很多API需要查阅相关书籍和博文才知道怎么用。内部代码比较乱。

建议:使用OpenSSL进行编程时,尽量使用更高级的EVP系列API(就是那堆以EVP_开头的函数),这是OpenSSL官方推荐的做法。

Botan

使用C++11编写的加密库。虽然目前使用C++11编写,但实际上其诞生于九十年代,也是比较老牌的加密库。

优点:包含的算法齐全,C++ API用起来很方便,其内部代码不像OpenSSL那样杂乱无章。提供FFI模块,可以用于多种语言,包括C和Python等。

缺点:管道/过滤器机制性能偏低,且未来不太可能改善。文档部分常用API没有示例,使用不便。

建议:C++项目建议使用此库。

libsodium

使用C语言编写,其目的是为其它高级的加密工具提供支持,因此跟上面提到的两个加密库有很大的不同。

优点:安全性和性能上相对于其它加密库更为激进。新版本只包含目前最安全的加密算法,比如密钥导出函数默认即是Argon2,AES只支持硬件加速的AES-256-GCM,诸如MD5、SHA1、RC4这些不安全的算法一概不提供。因此只使用其默认提供的算法即可达到很高的安全性,不容易因为误用算法导致安全漏洞或性能缺陷。

缺点:它的优点同时也是它的缺点,比较激进,可供选择的算法比较少,灵活性不如上面提到的加密库。

参考文献


PHP Manual FAQ: Safe Password Hashing
Wikipedia: Bcrypt
Dustin Boswell: Storing User Passwords Securely: hashing, salting, and Bcrypt

你可能感兴趣的:(密码学,算法)