构建软件的常用方式是分层,把问题域抽象为多层,每一层的概念定义为一组原语,上一层利用下一层的组件构造实现,并被上一层使用,层层叠叠即成软件。
例如在编程语言领域中,汇编语言为一层,在汇编上面是C/C++等静态编译语言,C/C++之上是python/php/lua等动态类型脚本语言层,之上常常还会构造领域特定的DSL
在网络架构中,以太网是一层,其上是ip协议的网络层,ip之上是tcp等传输层,tcp之上是http等应用层
密码学通信协议也是分层构造得到。大致可以这么分层:
最底层是基础算法原语的实现,例如: aes , rsa, md5, sha256,ecdsa, ecdh 等(举的例子都是目前的主流选择,下同)
其上是选定参数后,符合密码学里标准分类的算法,包括块加密算法,签名算法,非对称加密算法,MAC算法等,例如: aes-128-cbc-pkcs7,rsaes-oaep ,rsassa-pkcs1-v1_5, hmac-sha256,ecdsa-p256,curve25519 等
再其上,是把多种标准算法组合而成的半成品组件,例如:对称传输组件例如 aes-128-cbc + hmac-sha256,aes-128-gcm,认证密钥协商算法: rsassa-OAEP + ecdh-secp256r1,数字信封:rsaes-oaep + aes-cbc-128 + hmac-sha256 ,文件密码加密存储组件:pbkdf2+aes-128-cbc-hmac-sha256,密钥扩展算法 PRF-sha256 等
再其上,是用各种组件拼装而成的各种成品密码学协议/软件,例如:tls协议,ssh协议,srp协议,gnupg文件格式,iMessage协议,bitcoin协议等等
第1层,一般程序员都有所了解,例如rsa,简直路人皆知; md5 被广泛使用(当然,也有广泛的误用)
第2层,各种莫名其妙的参数,一般很让程序员摸不着头脑,需要深入学习才能理清。
第3层,很多程序员自己造的轮子,往往说白了就是想重复实现第3层的某个组件而已。
第4层,正确地理解,使用,部署这类成熟的开放协议,并不是那么容易。很多的误用来源于不理解,需要密码学背景知识,才能搞懂是什么,为什么,怎么用。
最难的是理论联系实际。面对一个一团乱麻的实际业务问题,最难的是从中抽象分析出其本质密码学问题,然后用密码学概念体系给业务建模。在分析建模过程中,要求必须有严密的,体系化的思考方式。不体系化的思考方式会导致疏漏,或者误用。
第2层中,密码学算法,常见的有下面几类:
块加密算法 block cipher: AES, Serpent, 等
流加密算法 stream cipher: RC4,ChaCha20 等
Hash函数 hash funtion:MD5,sha1,sha256,sha512 , ripemd 160,poly1305 等
消息验证码函数 message authentication code: HMAC-sha256,AEAD 等
密钥交换 key exchange: DH,ECDH,RSA,PFS方式的(DHE,ECDHE)等
公钥加密 public-key encryption: RSA,rabin-williams 等
数字签名算法 signature algorithm:RSA,DSA,ECDSA (secp256r1 , ed25519) 等
密码衍生函数 key derivation function: TLS-12-PRF(SHA-256) , bcrypto,scrypto,pbkdf2 等
随机数生成器 random number generators: /dev/urandom 等
每个类别里面的都有几个算法不断竞争,优胜劣汰,近几十年不断有老的算法被攻破被淘汰,新的算法被提出被推广。这一块话题广,水很深,内容多,陷阱也多,后续byron会翻译整理一系列文章,分享一下每一类里面个人收集的资料。
在此推荐一下 [ 开源电子书crypto101] 链接 https://www.crypto101.io/ ,讲的很透彻,而且很易读)
设计一个加密通信协议的过程,就是自顶向下,逐步细化,挑选各类组件,拼装成完整协议的过程
从上述分层的角度看,TLS大致是由3个组件拼成的:
1.对称加密传输组件,例如aes-128-gcm(这几个例子都是当前2015年最主流的选择);
2.认证密钥协商组件,例如rsa-ecdhe;
3.密钥扩展组件,例如TLS-PRF-sha256
这些组件可以再拆分为5类算法,在TLS中,这5类算法组合在一起,称为一个CipherSuite:
authentication (认证算法)
encryption (加密算法 )
message authentication code (消息认证码算法 简称MAC)
key exchange (密钥交换算法)
key derivation function (密钥衍生算法)
TLS协议设计之初就考虑到了这每一类算法的演变,所以没有定死算法,而是设计了一个算法协商过程,来允许加入新的算法( 简直是软件可扩展性设计的典范!),协商出的一个算法组合即一个CipherSuite
TLS CipherSuite 在 iana 集中注册,每一个CipherSuite分配有 一个2字节的数字用来标识 ,可以在 [iana的注册页面] 链接 https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 查看
在浏览器中,就可以查看当前使用了什么 CipherSuite,在地址栏上,点击一个小锁的标志,就可以看到了。
服务器端支持的CipherSuite列表,如果是用的openssl,可以用 openssl ciphers -V | column -t 命令查看,输出如:
例如其中这一行(这个是目前的主流配置):
0xC0,0x2F - ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
表示:
名字为ECDHE-RSA-AES128-GCM-SHA256
的CipherSuite ,用于 TLSv1.2版本,使用 ECDHE 做密钥交换,使用RSA做认证,使用AES-128-gcm做加密算法,MAC由于gcm作为一种aead模式并不需要,所以显示为aead,使用SHA256做PRF算法。
可以参考 [man 1 ciphers] 链接 http://linux.die.net/man/1/ciphers
要注意的是,由于历史兼容原因,tls标准,和openssl的tls实现中,有一些极度不安全的CipherSuite,一定要禁用,比如:
EXP , EXPORT
: 一定要禁用。EXPORT表示上世纪美国出口限制弱化过的算法,早已经被攻破,TLS的FREAK 攻击就是利用了这类坑爹的算法。
eNULL, NULL
: 一定要禁用。NULL表示不加密!默认是禁用的。
aNULL
: 一定要禁用。表示不做认证(authentication) ,也就是说可以随意做中间人攻击。
ADH
: 一定要禁用。表示不做认证的 DH 密钥协商。
上面是举个例子,读者不要自己去研究怎么配置,这太容易搞错。
请按照mozilla官方给出的这个[权威文档] 链接 https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_configurations ,复制粘贴就好了。
CipherSuite的更多解释,配置方法等,可以参考byron之前写的一篇文章 [SSL/TLS CipherSuite 介绍] https://blog.helong.info/blog/2015/01/23/ssl_tls_ciphersuite_intro/
TLS是用来做加密数据传输的,因此它的主体当然是一个对称加密传输组件。为了给这个组件生成双方共享的密钥,因此就需要先搞一个认证密钥协商组件,故,TLS协议自然分为:
做对称加密传输的record协议 ,the record protocol
做认证密钥协商的handshake协议,the handshake protocol
还有3个很简单的辅助协议:
changecipher spec 协议,the changecipher spec protocol, 用来通知对端从handshake切换到record协议(有点冗余,在TLS1.3里面已经被删掉了)
alert协议,the alert protocol, 用来通知各种返回码,
application data协议, The application data protocol,就是把http,smtp等的数据流传入record层做处理并传输。
这种 认证密钥协商 + 对称加密传输 的结构,是绝大多数加密通信协议的通用结构,在后文的更多协议案例中,我们可以看到该结构一再出现。
这5个协议中:
record协议在tcp流上提供分包,
图片来自网络:
其它的: handshake protocol, alert protocol, changeCipherSpec protocol, application data protocol都封装在record protocol的包里,然后在tcp上传输(此处以tcp举例,也有可能是udp,或者随便什么ipc机制等)
下文分别介绍,内容主要是翻译自 RFC5246,RFC5077,RFC4492