参考链接
- SSL通信双方如何判断对方采用了国密 - Bigben - 博客园
- 滑动验证页面 OpenSSL TLS1.2密码套件推荐
- 安全的TLS协议 | Hexo
OID
- OID是由ISO/IEC、ITU-T国际标准化组织上世纪80年代联合提出的标识机制,其野心很大,为任何类型的对象(包括实体对象、虚拟对象和组合对象)进行全球唯一命名。
- 通过唯一的编码,我们就可以识别出对象。但要为所有对象进行唯一命名,其难度和工作量都很大,所以它采用了分层树形结构。
- OID对应于“OID树”或层次结构中的一个节点,该节点是使用ITU的OID标准X.660正式定义的。
- 树的根包含以下三个起点:0:ITU-T1:ISO2:ITU-T/ISO联合发布
- 树中的每个节点都由一系列由句点分隔的整数表示。比如,表示英特尔公司的OID如下所示:1.3.6.1.4.1.343
- 1 ISO1.3 识别组织1.3.6 美国国防部1.3.6.1 互联网1.3.6.1.4 私人1.3.6.1.4.1 IANA企业编号 1.3.6.1.4.1.343 因特尔公司
-  这里采用分而治之的策略,解决编码重复问题。树中的每个节点均由分配机构控制,该机构可以在该节点下定义子节点,并为子节点委托分配机构。在上面的例子中,根节点“1”下的节点号由ISO分配,“1.3.6”下的节点由美国国防部分配,“1.3.6.1.4.1”下的节点由IANA分配,“1.3.6.1.4.1.343”下的节点由英特尔公司分配,依此类推。只要有需求,可以一直往下分配下去,也解决了编码不够的问题。 
- 在实际应用中,ISO/IEC国际标准化机构维护顶层OID标签,各个国家负责该国家分支下的OID分配、注册和解析等工作,实现自我管理和维护。相应的,针对国密,国家密码局也定义了各类对象的标识符:
- GMT 0006-2012 密码应用标识规范.pdf GM-Standards/GMT 0006-2012 密码应用标识规范.pdf at master · guanzhi/GM-Standards · GitHub


- 像"1.2.156.10197.1.100"这种字符串,人读起来比较直观,但对于计算机对字符串处理的效率非常低,所以在程序代码中,对OID又进行了一次编码。
- 通过 objects.pl 脚本,生成 obj_data.h文件,这个才是在代码中使用到的OID编码。 boringssl也类似,不过其采用了 go 语言编写的转换脚本。
- 在GmSSL源码中,原始的OIDs定义在crypto/objects/objects.txt文件中,在文件的尾部,我们可以看到国密的相关定义:
国密相关定义
sm-scheme 102 1		: SM1-ECB		: sm1-ecb
sm-scheme 102 2		: SM1-CBC		: sm1-cbc
!Cname sm1-ofb128
sm-scheme 102 3		: SM1-OFB		: sm1-ofb
!Cname sm1-cfb128
sm-scheme 102 4		: SM1-CFB		: sm1-cfb
sm-scheme 102 5		: SM1-CFB1		: sm1-cfb1
sm-scheme 102 6		: SM1-CFB8		: sm1-cfb8# SM2 OIDs
sm-scheme 301		: sm2p256v1
sm-scheme 301 1		: sm2sign
sm-scheme 301 2		: sm2exchange
sm-scheme 301 3		: sm2encryptsm-scheme 501		: SM2Sign-with-SM3	: sm2sign-with-sm3
sm-scheme 502		: SM2Sign-with-SHA1 	: sm2sign-with-sha1
sm-scheme 503		: SM2Sign-with-SHA256	: sm2sign-with-sha256
sm-scheme 504		: SM2Sign-with-SHA511	: sm2sign-with-sha512
sm-scheme 505		: SM2Sign-with-SHA224	: sm2sign-with-sha224
sm-scheme 506		: SM2Sign-with-SHA384	: sm2sign-with-sha384
sm-scheme 507		: SM2Sign-with-RMD160	: sm2sign-with-rmd160
sm-scheme 520		: SM2Sign-with-Whirlpool : sm2sign-with-whirlpool
sm-scheme 521		: SM2Sign-with-Blake2b512 : sm2sign-with-blake2b512
sm-scheme 522		: SM2Sign-with-Blake2s256 : sm2sign-with-blake2s256sm2encrypt 1		: sm2encrypt-recommendedParameters
sm2encrypt 2		: sm2encrypt-specifiedParameters
sm2encrypt 2 1		: sm2encrypt-with-sm3
sm2encrypt 2 2		: sm2encrypt-with-sha1
sm2encrypt 2 3		: sm2encrypt-with-sha224
sm2encrypt 2 4		: sm2encrypt-with-sha256
sm2encrypt 2 5		: sm2encrypt-with-sha384
sm2encrypt 2 6		: sm2encrypt-with-sha512
sm2encrypt 2 7		: sm2encrypt-with-rmd160
sm2encrypt 2 8		: sm2encrypt-with-whirlpool
sm2encrypt 2 9		: sm2encrypt-with-blake2b512
sm2encrypt 2 10		: sm2encrypt-with-blake2s256
sm2encrypt 2 11		: sm2encrypt-with-md5# SM3
sm-scheme 401		: SM3			: sm3
sm-scheme 401 2		: HMAC-SM3		: hmac-sm3# SM4
sm-scheme 104 1		: SMS4-ECB		: sms4-ecb
sm-scheme 104 2		: SMS4-CBC		: sms4-cbc
!Cname sms4-ofb128
sm-scheme 104 3		: SMS4-OFB		: sms4-ofb
!Cname sms4-cfb128
sm-scheme 104 4		: SMS4-CFB		: sms4-cfb
sm-scheme 104 5		: SMS4-CFB1		: sms4-cfb1
sm-scheme 104 6		: SMS4-CFB8		: sms4-cfb8
sm-scheme 104 7		: SMS4-CTR		: sms4-ctr
sm-scheme 104 8		: SMS4-GCM		: sms4-gcm
sm-scheme 104 9		: SMS4-CCM		: sms4-ccm
sm-scheme 104 10	: SMS4-XTS		: sms4-xts
sm-scheme 104 11	: SMS4-WRAP		: sms4-wrap
sm-scheme 104 12	: SMS4-WRAP-PAD		: sms4-wrap-pad
sm-scheme 104 100	: SMS4-OCB		: sms4-ocb# SM5/6/7/8
sm-scheme 201		: SM5			: sm5
sm-scheme 101 1		: SM6-ECB		: sm6-ecb
sm-scheme 101 2		: SM6-CBC		: sm6-cbc
!Cname sm6-ofb128
sm-scheme 101 3		: SM6-OFB		: sm6-ofb
!Cname sm6-cfb128
sm-scheme 101 4		: SM6-CFB		: sm6-cfb
!Alias sm7 sm-scheme 105
!Alias sm8 sm-scheme 106# SM9
sm-scheme 302		: id-sm9PublicKey
sm-scheme 302 1		: sm9sign
sm-scheme 302 2		: sm9keyagreement
sm-scheme 302 3		: sm9encrypt
sm-scheme 302 4		: sm9hash1
sm-scheme 303 7		: sm9hash2
sm-scheme 302 5		: sm9kdf
sm-scheme 302 6		: id-sm9MasterSecret
sm-scheme 302 6	1 	: sm9bn256v1
sm9sign 1		: sm9sign-with-sm3
sm9sign 2		: sm9sign-with-sha256
sm9encrypt 1		: sm9encrypt-with-sm3-xor
sm9encrypt 2		: sm9encrypt-with-sm3-sms4-cbc
sm9encrypt 3		: sm9encrypt-with-sm3-sms4-ctr
sm9hash1 1		: sm9hash1-with-sm3
sm9hash1 2		: sm9hash1-with-sha256
sm9hash2 1		: sm9hash2-with-sm3
sm9hash2 2		: sm9hash2-with-sha256
sm9kdf 1		: sm9kdf-with-sm3
sm9kdf 2		: sm9kdf-with-sha256密码套件
- 仅仅定义了OID还不够,因为国密并不是一个单一的标准,包含了很多加密、解密、哈希等算法,可以形成很多种组合,不能简单假定对方采用了国密就可以建立通信。
- 在SSL通信开始,双方就需要进行协商,采用何种算法进行通信。这就引出了密码套件(CipherSuite)的概念。
- 密码套件是一系列密码学算法的组合,主要包含多个密码学算法: - 密钥交换算法:用于握手过程中建立信道,一般采用非对称加密算法
- 数据加密算法:用于信道建立之后的加密传输数据,一般采用对称加密算法
- 消息验证算法:用于验证消息的完整性,包括整个握手流程的完整性(例如TLS握手的最后一步就是一个对已有的握手消息的全盘哈希计算的过程)
 
- 密码套件的构成如下图所示:

- /home/chy-cpabe/Downloads/GmSSL-master/README.md 文件内部包含了国密算法套件的定义

- 代码可以参考 gmtls.h 文件,也可以使用GMSSL的命令查看所支持的密码套件: openssl ciphers -V | column -t
- 如果使用gmssl,将openssl替换成gmssl即可,如果未配置全局路径,需要到编译后生成的可执行脚本处执行上述命令
 
- 第一列:数值代表密码套件的编号,每个密码套件的编号由IANA定义。
- 第二列:代表密码套件的名称,虽然密码套件编号是一致的,不同的TLS/SSL协议实现其使用的名称可能是不一样的。
- 第三列:表示该密码套件适用于哪个TLS/SSL版本的协议。
- 第四列:表示密钥协商算法。
- 第五列:表示身份验证算法。
- 第六列:表示加密算法、加密模式、密钥长度。
- 第七列:表示HMAC算法。其中AEAD表示采用的是AEAD加密模式(比如AES128-GCM),无须HMAC算法。
操作流程
- 一个密码学套件是完成整个TLS握手的关键。在TLS握手的时候ClientHello里面携带了客户端支持的密码学套件列表,ServerHello中携带了Server根据Client提供的密码学套件列表中选择的本地也支持的密码学套件。
- 也就是说选择使用什么密码学套件的选择权在Server的手里,这句话感觉有问题,客户端先选择密码学套件列表,看服务器是否支持,或者服务器考虑到算法的安全性会禁用不安全的密码学套件 我的这句话有问题,借此提醒
注意事项
- 值得注意的是,这里的编码又没有采用OID,这也是开发过程中需要注意的,不同的地方使用了不同标准规范,需要在开发中翻阅相应的协议和规范。
- 可以看出,GmSSL并没有实现所有的国密的密码套件,但同时又扩充了几个标准未定义的密码套件,比如ECDHE-SM2-WITH-SMS4-GCM-SM3、ECDHE-SM2-WITH-SMS4-SM3等。这就体现出协商的重要性了,对双方所支持的密码套件取一个交集,从中选择一个。如果不存在交集,协商也就不成功。注意,协商过程中服务器端可能会禁用一些不太安全的密码套件(比如历史遗留的一些现今已不太安全的算法),这时即使双方都支持,也可能协商不成功。
- 我们可以测试服务器是否支持某个特定的密码套件:

代码编写
- 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"); 
- 如果服务端只设置了一种加密套件,那么客户端要么接受要么返回错误。
- 加密套件的选择是由服务端做出的。客户端没有权利指定其他加密套件。