起点
最近空闲时间都在研究Google开源项目Tink的源码,发现很多密码学相关概念似懂非懂,直接导致越看越蒙圈。在通过谷歌度娘恶补基础知识的过程中,发现密码学理论艰深,概念繁多。写这篇文章就是希望把关键点记录下来,一来可以加深印象,留着以后温故而知新;二来也许可以给有同样需求的朋友一点捷径。
这篇文章注定会很长,因为我会尽量把每个概念或名词都附上详细解释,而我又不想分开来写。本文是我在翻阅了很多网络文章后,加入自己的理解后整理而成。所有出处和参考资料都在文章结尾,侵删!
本人水平有限,错误难免,如果您发现错误,请务必指出,非常感谢!
密码学的方法论
密码学和软件开发不同,软件开发是工程,是手艺,造轮子是写代码的一大乐趣。开发过程中常常要做出多方面的权衡(例如CAP),难有明确的对错。
密码学就不一样了。密码学是科学,不是工程,有严格的技术规范,严禁没有经过学术训练者随意创造。要有严谨的理论建模,严密的数学证明。少有需要权衡的地方,正确就是正确,错误就是错误。
密码学有很多陷阱,设计密码学协议或者软件,是极其容易出错的高风险专业活动,单纯的码农背景是做不了的。本着不作死就不会死的伟大理念,首先推荐读者尽可能使用 TLS 这种标准化、开源、使用广泛、久经考验、高性能协议。
经过几十年的军备竞赛式发展,已经发展出大量巧妙而狡猾的攻击方法,我们使用的算法,都是在所有已知攻击方法下都无法攻破的,由于大多数码农并没有精力去了解最前沿的攻击方法,我们也就没有能力去评价一个加密算法,更没有能力自己发明算法。所以最好跟着业界的主流技术走,肯定不会有大错。
现在搞现代密码学研究的主要都是数学家,在这领域里,以一个码农的知识背景,已经很难理解最前沿的东西,连正确使用加密算法都是要谨慎谨慎再谨慎。一个码农,能了解密码学基本概念,跟进密码学的最新应用趋势,并正确配置部署TLS这种协议,就很不错了。
本文也只是整理一些粗浅的密码学常识,读完这篇文章,并不能使你具有设计足够安全的密码学能力。
上面提到过的密码学算法很难被正确地使用,各种细节非常容易出错。 例如:
- 大多数码农都听说过aes,可是你又了解多少细节呢,比如:aes应该用哪种模式?应该用哪种padding?IV/nonce应该取多少bit?IV/nonce应该怎么生成? key size应该选多大?key应该怎么生成?应不应该加MAC?MAC算法的选择?MAC和加密应该怎么组合?
- 大多数知道RSA的码农分不清 RSASSA-PKCS1-v1_5 、RSAES-OAEP 和 RSASSA-PSS?
- 更多错误参见 stackoverflow问答,强烈推荐仔细阅读
有没有被这一连串的问题弄懵逼?点开 stackoverflow 之后更是不知所云,备受打击?没关系,下面跟我一起来完成这个补完计划吧。
下文我提到的每个算法都是当前安全的,哪些过时或被破解的不在本文讨论范围内
在各种适用场景下,你应该使用的现代密码学算法:
加密数据
适用场景:需要避免把明文数据在网络上传输的时候。
推荐选择:(1) 首选NaCl,或者libsodium。使用crypto_secretbox() 或 crypto_secretbox_open() 函数 ;(2) Chacha20-Poly1305算法;(3) AES-GCM 算法;
以上3种算法,都是AEAD类的算法,目前来看是最好的选择。
并且,我们还应该:
- 避免AES-ECB
- 避免AES-CTR
- 避免64bit块大小的块加密算法
- 避免OFB模式
- 不要使用RC4,RC4已经被攻破
我们先看看 AES-ECB 和 AES-CTR 存在什么问题,再看 AES-GCM 是怎么解决的。
- Electronic Codebook Book (ECB)
电码本模式,将整个明文进行分组,分组长度可为128,256,或512bits,然后对每个小组进行加密。
可以看出,明文中重复的排列会反映在密文中。并且,当密文被篡改时,解密后对应的明文分组也会出错,且解密者察觉不到密文被篡改了。也就是说,ECB不能提供对密文的完整性校验。
因此,在任何情况下都不推荐使用ECB模式。
Ek
使用秘钥k对输入做对称加密运算
- CounTeR (CTR)
计数器模式,我们不再对密文进行加密,而是对一个逐次累加的计数器进行加密,用加密后的比特序列与明文分组进行 XOR 得到密文。过程如下图:
该模式下,每次与明文分组进行XOR的比特序列是不同的,因此,计数器模式解决了ECB模式中,相同的明文会得到相同的密文的问题。
但CTR仍然不能提供密文消息完整性校验的功能。
有的人可能会想到,如果将密文的hash值随密文一起发送,密文接收者对收到的密文计算hash值,与收到的hash值进行比对,这样是否就能校验消息的完整性呢?
再仔细想想,就能发现这其中的漏洞。当篡改者截获原始的密文消息时,先篡改密文,而后计算篡改后的密文hash, 替换掉原始消息中的密文hash。这样,消息接收者仍然没有办法发现对源密文的篡改。
可见,使用单向散列函数计算hash值仍然不能解决消息完整性校验的问题。
XOR
异或,英文为exclusive OR。是一个数学运算符,应用于逻辑运算。数学符号为“⊕”,计算机符号为“xor”。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法。
运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1)。
MAC (Message Authentication Code)
想要校验消息的完整性,必须引入另一个概念:消息验证码。消息验证码是一种与秘钥相关的单项散列函数。
密文的收发双方需要提前共享一个秘钥,例如当韩梅梅向李雷发送消息m时,不仅仅发送消息m,还要发送一个由MAC函数计算得到的MAC值。李雷会用自己的密钥对收到的消息m再次进行MAC计算,并判断这个值与收到的MAC值是否相等,如果不匹配则说明可能消息m被篡改了。由于窃听者不知道秘钥,所以不能为篡改后的消息计算出正确的MAC值。-
GMAC (Galois message authentication code mode)
伽罗瓦消息验证码,GMAC就是利用伽罗华域(Galois Field,GF,有限域)乘法运算来计算消息的MAC值。假设秘钥长度为128bits, 当密文大于128bits时,需要将密文按128bits进行分组。流程如下图:
Mh
将输入与秘钥h在有限域GF(2^128)上做乘法
- GCM (Galois/Counter Mode)
AES-GCM中的G就是指GMAC,C就是指CTR。是一种AEAD,是目前TLS的主力算法,互联网上https流量的大部分依赖使用AES-GCM。
GCM可以提供对消息的加密和完整性校验,另外,它还可以提供附加消息的完整性校验。在实际应用场景中,有些信息是我们不需要保密,但信息的接收者需要确认它的真实性的,例如源IP、源端口、目的IP和IV等等。因此,我们可以将这一部分作为附加消息加入到MAC值的计算当中。
AEAD
在通常的密码学应用中,Confidentiality (保密) 用加密实现,Message authentication (消息认证) 用MAC实现。这两种算法的配合方式,引发了很多安全漏洞,过去曾经有3种方法:
- Encrypt-and-MAC
- MAC-then-Encrypt
- Encrypt-then-MAC
后来发现,1和2都是有安全问题,所以2008年起,逐渐提出了“用一个算法在内部同时实现cipher+MAC”的idea,称为 AEAD(Authenticated encryption with additional data)。 在这种概念里,cipher+MAC 被 一个AEAD算法替换。Authenticated encryption
我们再看看另外几个推荐的加密库
NaCl
NaCl 是密码学学术权威 Daniel J. Bernstein教授设计的一个密码学算法库,2008年发开始公布。NaCl的特点是:api简洁而易用,高性能,高安全性,主要用于网络通信、加密、解密、签名等,NaCl提供了构建高层密码学工具的核心功能。目前仅有C和Python版。
libsodium
libsodium 是对NaCl库的一个分支,进一步改进接口易用性,和可移植性。网站看上去清爽多了,还提供了GitHub地址
ChaCha20-poly1305
是一种AEAD,提出者是Daniel J. Bernstein教授。由 ChaCha20流密码 和 Poly1305消息认证码( MAC )结合的一种应用在互联网安全协议中的认证加密算法,由Google公司率先在Andriod移动平台中的Chrome中代替 RC4 使用。由于其算法精简、安全性强、兼容性强等特点,目前Google致力于全面将其在移动端推广。
对称签名
适用场景:安全加固一个API,各种开放API的调用方认证
如果你需要对一个API做认证( authenticating ),但是不需要做加密( encrypting ),记得千万不要自己发明算法,你自己发明的MAC算法基本都有安全漏洞,如果不信,请Google一下 “长度扩展攻击” 长度扩展攻击 ,Flickr的漏洞案例
同时,必须要注意的是,要使用一个常数时间字符串对比算法(这个地方和码农的常识完全相反,请务必留意)
此外,应该
- 避免自行设计的“带密码的hash”结构,你的设计基本都是有安全漏洞;
- 避免HMAC-MD5,避免HMAC-SHA1,使用HMAC-SHA256, HMAC-SHA512等;
- 避免复杂的多项式MAC;
- 避免加密hash值的结构;
- 避免CRC;
未完待续……