OpenSSL TLS1.2密码套件推荐
以下段落摘抄自知乎专栏
原文链接 https://zhuanlan.zhihu.com/p/37239435
原作者 刘叔
正文节选
由于对于TLS1.2来说,服务端主要支持的密码学就那么几种,所以一个常见的写法是固定的写:
ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:RSA+AES128:!aNULL:!eNULL:!LOW:!ADH:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS
优先选择ECC证书和ECDHE握手的方式,如果客户端不支持或者只有RSA证书,就会选择RSA证书和ECDHE密钥交换算法,而一直没有选择RSA的密钥交换算法。只有在ECDHE密钥交换算法完全不支持之后才会去可能使用RSA来进行密钥交换
(RSA密钥交换、DH以及DHE密钥交换算法在实际的应用相对在减少。ECDHE逐渐成为主流。)
TLS1.3的定义和TLS1.2的定义略有不同,这里着重介绍1.2的定义。
一个密码学套件是完成整个TLS握手的关键。在TLS握手的时候ClientHello里面携带了客户端支持的密码学套件列表,ServerHello中携带了Server根据Client提供的密码学套件列表中选择的本地也支持的密码学套件。也就是说选择使用什么密码学套件的选择权在Server的手里(在使用nginx的时候把ssl_prefer_server_ciphers
配置项打开),而Server通常可以通过配置文件指定Server要支持的密码学套件的列表和顺序。为此需求,OpenSSL专门定义了一套比较难懂的定义方法,Nginx的密码学套件的配置方法也只是对OpenSSL定义的定义形式的透传。也就是说使用Nginx定义的ssl_prefer_server_ciphers
配置项是原封不动的传输给OpenSSL的。使用OpenSSL的工具程序就可以验证分析一个密码学套件字符串:openssl -v cipher 'RC4:HIGH:!aNULL:!MD5'
,就可以看到RC4:HIGH:!aNULL:!MD5
这个密码学套件配置的详细内容。这个命令是专门用于解析OpenSSL的密码学套件的配置的工具,由于字符串格式的配置比较难学习,使用这个命令可以起到有效的帮助。
密码套件分为三大部分:
- 密钥交换算法
- 数据加密算法
- 消息验证算法(MAC:Message Authentication Code)。
密钥交换算法用于握手过程中建立信道,一般采用非对称加密算法。
数据加密算法用于信道建立之后的加密传输数据,一般采用对称加密算法。
消息验证算法是一种哈希,用于验证消息的完整性,包括整个握手流程的完整性(例如TLS握手的最后一步就是一个对已有的握手消息的全盘哈希计算的过程)。
在OpenSSL的配置文件里写密码学套件的写法和实际在OpenSSL代码里通过解析配置生成的宏套件代码是不同的。OpenSSL的配置文件的写法是一个生成密码学套件序列的文本引擎,而实际的密码学的套件名字却是OpenSSL代码里的宏定义,这个宏定义的名字是和RFC中规定的命名方式是一样的。
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
是一个密码学套件的标准名字。这里的TLS代表的是TLS协议,如果未来TLS改名,这个名字可能会变,否则会一直是这个名字。WITH是一个分隔单次,WITH前面的表示的是握手过程所使用的非对称加密方法,WITH后面的表示的是加密信道的对称加密方法和用于数据完整性检查的哈希方法。WITH前面通常有两个单次,第一个单次是约定密钥交换的协议,第二个单次是约定证书的验证算法。要区别这两个域,必须要首先明白,两个节点之间交换信息和证书本身是两个不同的独立的功能。两个功能都需要使用非对称加密算法。交换信息使用的非对称加密算法是第一个单词,证书使用的非对称加密算法是第二个。有的证书套件,例如TLS_RSA_WITH_AES_256_CBC_SHA
,WITH单词前面只有一个RSA单词,这时就表示交换算法和证书算法都是使用的RSA,所以只指定一次即可。可选的主要的密钥交换算法包括: RSA, DH, ECDH, ECDHE。可选的主要的证书算法包括:RSA, DSA, ECDSA。两者可以独立选择,并不冲突。AES_256_CBC
指的是AES这种对称加密算法的256位算法的CBC模式,AES本身是一类对称加密算法的统称,实际的使用时要指定位数和计算模式,CBC就是一种基于块的计算模式。最后一个SHA就是代码计算一个消息完整性的哈希算法。
对于OpenSSL中对密码学套件的定义,需要从一个设计者的角度来考虑和理解,如果我来设计这个字符串引擎,我该如何的让这一串字符串能代表我要指定的一系列密码学套件?
一个最显然的方法,就是我直接指定我要使用的密码学套件的列表。由于一个密码学套件就有五六个变量,这种制定方法会非常的长,这么长的配置是对配置者非常不友好的,而且并不是所有要使用Nginx的都明白密码学套件是什么,每一种之间的区别在哪。但是这种逐个指定的模式也必须支持,这就是配置引擎的第一个符号冒号,使用冒号来形成列表,逐个的指定密码学套件是一个最显著的需求。同时,由于密码学套件的五六个可变的域,所以使用正则表达式或许可以成为一个选择,但是密码学套件是一个从有效的选项中选择的过程,显然正则匹配又不合适。所以,另外一个思路就产生了,除了可以指定完整的密码学套件的名字,还可以在列表中直接用排除法写明支持某一种哈希,或者排除某一种哈希,这样所有使用这种哈希,或者不使用这种哈希的密码学套件就能够被一次性的选择或者排除。显然选择和排除可以同时出现,排除肯定是比选择有更高的优先级的。这也就是OpenSSL密码学套件定义字符串的感叹号,感叹号表示永久性的排除某一个密码学算法。并且冒号分割的列表中,不但可以指定完整的密码学套件,还可以指定某一种对称加密,或者是哈希算法等单个独立的密码学算法,表示我愿意使用包含这种密码学算法的所有套件。如果给它加个感叹号,就表示我不愿意使用。
使用了感叹号,问题又出现了。我要给什么东西加感叹号?任何一种密码学算法是一个方面,但是有很多类别是否可以一次性定义?为此OpenSSL定义了一系列的配置变量,只使用这一系列的变量就可以指定特定的密码学算法或者某个常用的密码学算法的集合。
完整的支持的变量列表可以在OpenSSL官方网站和man手册中找到,例如https://www.openssl.org/docs/man1.0.2/apps/ciphers.html 网址中定义的一系列变量。比如DEFAULT变
量,这个变量相当于套件序列:ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2
。这个序列里面又包含了其他变量的使用,例如ALL变量就表示出了eNULL之外的所有密码学套件,而eNULL就表示所有没有加密算法的密码学套件,也就是所有明文传输的密码学套件。EXPORT表示的所有的出口强度的密码学。这里的感叹号就表示了从ALL指定的所有密码套件中排除掉出口强度的密码学。出口强度是一个非常低的强度,出口的密码学强度都是有关部门确定可以直接破解的加密方法(否则为什么要出口?)。美国国内就严格要求了可以出口的密码学的强度,OpenPGP就是因为密码学的强度过大所以以出口高强度密码学在美国被起诉了,不过作者最后把整个代码出版成一本书,并没有出口密码学,而是出口书籍知识,用这种方法完美的绕过了美国的密码学限制法律。LOW表示的所有已经被认为的可以破解的弱强度密码,这部门密码和EXPORT密码是不重合的,但是两部分加起来就几乎是极度不可靠密码套件的全体了。在OpenSSL中都是默认根本不编译启用的。aNULL表示的是所有的不包含认证的密码学套件。类似的变量还有很多,就不一一列举。
在密码学套件的字符串里面,还有一个加号“+”和一个减号“-”两个操作符号,也是用于操作变量的,但是使用的比较少。在man手册中也有相关的介绍。
由于现在浏览器的寡头现象特别严重,每一种浏览器支持的加密套件基本都是固定的通用的一些,所以服务器在选择自己支持什么加密套件的时候,应当参考不同的客户端发来ClientHello时携带的客户端支持的套件集。虽然总体的种类很多,但是一段时间实际通用的却很少。例如在当前TLS1.2的环境下,证书也就是RSA和ECDSA两种选择,握手也基本上只有RSA和ECDHE两种选择,加密算法也一般只会选择AES-GCM(TLS1.2废弃了IDEA和DES相关的加密套件),哈希算法也大部分情况下使用SHA256。所以实际的变化并不太多,可能使用一个完整密码学套件的冒号列表就可以完全表达了。指定的各个密码学套件的先后顺序就是服务器选择使用哪个密码学套件的先后顺序。
Chrome浏览器默认支持的TLS密码套件上文摘抄自知乎专栏 https://zhuanlan.zhihu.com/p/37239435
以下摘抄自博主kelsel的文章:
https://blog.csdn.net/kelsel/article/details/52758432
Openssl定义了4中选择符号:“+”,“-”,“!”,“@”。其中,“+”表示取交集;“-”表示临时删除一个算法;“!”表示永久删除一个算法;“@“表示了排序方法。
多个描述之间可以用“:”或“,”或空格或“;”来分开。选择加密套件的时候按照从左到的右顺序构成双向链表,存放与内存中。
ALL:!DH:RC4+RSA:+SSLv2:@STRENGTH
表示的意义是:首先选择所有的加密套件,然后在得到的双向链表之中去掉密钥交换采用DH的加密套件;加入包含RC4对称加密算法与RSA身份认证算法的交集加密套件;再将支持老版本SSLv2的加密套件放在尾部;最后,@STRENGTH
表示将得到的结果按照安全强度进行排序。
SSL建立链接之前,客户端和服务器端用openssl函数来设置自己支持的加密套件。主要的函数有:
int SSL_set_cipher_list(SSL *s, const char *str);
int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str);
比如只设置一种加密套件:
int ret=SSL_set_cipher_list(ssl,"RC4-MD5");
如果服务端只设置了一种加密套件,那么客户端要么接受要么返回错误。加密套件的选择是由服务端做出的。客户端没有权利指定其他加密套件。
来源参考:
- CSDN博主kelsel的文章:https://blog.csdn.net/kelsel/article/details/52758432