国密SSL
本文讨论 TLCP(国密 SSL)。
国密算法
国密算法还不少,包括 SM1、SM2、SM3、SM4、SM7、SM9、ZUC 等等。我还查了一下,SM 的意思肯定不是那个啥,而是 ShangMi(商密)……
- SM1 是个非公开的对称密码算法,由硬件实现
- SM2 是个公钥密码算法
- SM3 是个摘要算法
- SM4 是个对称密码算法
- SM7 是个非公开的对称密码算法,用于非接触式 IC 卡等
- SM9 是个非对称密码算法
- ZUC 是个流加密算法
SM2
SM2 实际上就是个 ECC,wiki 上也说它是 an Elliptic Curve Diffie-Hellman key agreement and signature using a specified 256-bit elliptic curve,但网上很多资料却偏要说什么它对标 RSA,这也没什么好对标的。正好我有点忘了,那就趁机复习一遍 ECC:
设有有限域 \(F_p\) 上的椭圆曲线 \(y^2=x^3+ax+b \mod p\),点集 \(E_p(a,b)\) 表示曲线上的整数点。对于点集中的任意点 \(P\),易得 \(Q=kP\) 的值。但给定 \(Q\) 和 \(P\),计算 \(k\) 则十分困难,构成了椭圆曲线离散对数问题(ECDLP)。
加解密流程如下:
- 选择椭圆曲线 \(E_p(a,b)\),将明文 \(m\) 通过嵌入到椭圆曲线上得点 \(P_m\)
- 取 \(E_p(a,b)\) 的一个生成元 \(G\),\(E_p(a,b)\) 和 \(G\) 作为公开参数
- Alice 选 \(n_A\) 作为私钥,并以 \(P_A=n_AG\) 作为公钥
- Bob 向 Alice 发送消息 \(P_m\),可选取随机数 \(k\),产生点对 \(C_m=\left\{kG,P_m+kP_A\right\}\) 作为密文
- Alice 以密文点对中的第二个点减去其私钥与第一个点倍乘的结果,即 \((P_m+kP_A)-n_AkG=P_m+k(n_AG)-n_AkG =P_m\)
对于 SM2,其使用的椭圆曲线参见 GB/T 32918.5-2017 《信息安全技术SM2椭圆曲线公钥密码算法第5部分:参数定义》。
SM3
SM3 是个密码杂凑算法,介绍它具体的参数没有什么意义。详见 GB/T 32905-2016《信息安全技术SM3密码杂凑算法》
SM4
SM4 用的是分组对称加密,同样的,介绍它具体的参数也没有什么意义。详见 GB/T 32907-2016《信息安全技术SM4分组密码算法》
TLCP
传输层密码协议 Transport Layer Cryptography Protocol (TLCP) 采用SM系列密码算法和数字证书等密码技术。其被定义于《GB/T 38636-2020 信息安全技术 传输层密码协议》,于 2020 年 4 月发布,在 2020 年 11 月实施。其由国密 SSL(《GM/T 0024-2014》)发展而来。
TLCP 和 TLS 主要有以下不同之处:
密码套件
和普通 TLS 不同,TLCP 使用了国密套件:
- 密钥交换时,可以选择基于 SM2 的 ECC/ECDHE、基于 SM9 的 IBC/IBSDH、RSA
- 加密算法为 SM4,提供 CBC 和 GCM 两种模式
- 完整性校验使用 SM3 或 SHA-256
GB/T 38636-2020 6.4.5.2.1 定义了目前TLCP协议支持所有密码套件如下所示:
名称 | 密钥交换 | 加密 | 效验 | 值 |
---|---|---|---|---|
ECDHE_SM4_CBC_SM3 | ECDHE | SM4_CBC | SM3 | {0xe0,0x11} |
ECDHE_SM4_GCM_SM3 | ECDHE | SM4_GCM | SM3 | {0xe0,0x51} |
ECC_SM4_CBC_SM3 | ECC | SM4_CBC | SM3 | {0xe0,0x13} |
ECC_SM4_GCM_SM3 | ECC | SM4_GCM | SM3 | {0xe0,0x53} |
IBSDH_SM4_CBC_SM3 | IBSDH | SM4_CBC | SM3 | {0xe0,0x15} |
IBSDH_SM4_GCM_SM3 | IBSDH | SM4_GCM | SM3 | {0xe0,0x55} |
IBC_SM4_CBC_SM3 | IBC | SM4_CBC | SM3 | {0xe0,0x17} |
IBC_SM4_GCM_SM3 | IBC | SM4_GCM | SM3 | {0xe0,0x57} |
RSA_SM4_CBC_SM3 | RSA | SM4_CBC | SM3 | {0xe0,0x19} |
RSA_SM4_GCM_SM3 | RSA | SM4_GCM | SM3 | {0xe0,0x59} |
RSA_SM4_CBC_SHA256 | RSA | SM4_CBC | SHA256 | {0xe0,0x1c} |
RSA_SM4_GCM_SHA256 | RSA | SM4_GCM | SHA256 | {0xe0,0x5a} |
证书
区别于 TLS 协议,TLCP 协议要求服务端需要使用 2 对非对称密钥对以及 2 张证书,它们分别是:
- 签名密钥对、签名证书,用于身份认证
- 加密密钥对、加密证书,用于密钥交换
其中,加密密钥对应由外部密钥管理机构(KMC)产生并由外部认证机构签发加密证书。
我们将签名密钥对与加密密钥对统称为服务端密钥 。
TLCP 使用
标准版本的 OpenSSL 是不支持 TLCP 的,我们假设已经拥有了魔改过的 OpenSSL。
生成 SM2 双证书
编写 openssl.cnf
,这里直接借用 OpenEuler 写好的:
HOME = .
oid_section = new_oids
[ new_oids ]
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
x509_extensions = usr_cert # The extensions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options
cert_opt = ca_default # Certificate field options
default_days = 365 # how long to certify for
default_crl_days = 30 # how long before next CRL
default_md = default # use public key default MD
preserve = no # keep passed DN ordering
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extensions to add to the self signed cert
string_mask = utf8only
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = AU
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Internet Widgits Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature
[ v3enc_req ]
basicConstraints = CA:FALSE
keyUsage = keyAgreement, keyEncipherment, dataEncipherment
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical,CA:true
keyUsage = cRLSign, keyCertSign
[ crl_ext ]
authorityKeyIdentifier = keyid:always
[ proxy_cert_ext ]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
proxyCertInfo = critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
####################################################################
[ tsa ]
default_tsa = tsa_config1 # the default TSA section
[ tsa_config1 ]
dir = ./demoCA # TSA root directory
serial = $dir/tsaserial # The current serial number (mandatory)
crypto_device = builtin # OpenSSL engine to use for signing
signer_cert = $dir/tsacert.pem # The TSA signing certificate
certs = $dir/cacert.pem # Certificate chain to include in reply
signer_key = $dir/private/tsakey.pem # The TSA private key
signer_digest = sha256 # Signing digest to use.
default_policy = tsa_policy1 # Policy if request did not specify it
other_policies = tsa_policy2, tsa_policy3 # acceptable policies
digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory)
accuracy = secs:1, millisecs:500, microsecs:100
clock_precision_digits = 0 # number of digits after dot.
ordering = yes # Is ordering defined for timestamps?
tsa_name = yes # Must the TSA name be included in the reply?
ess_cert_id_chain = no # Must the ESS cert id chain be included?
ess_cert_id_alg = sha1 # algorithm to compute certificate
自签名 CA:
openssl ecparam -name SM2 -out SM2.pem
openssl req -config ./openssl.cnf -nodes -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=root ca' -keyout CA.key -newkey ec:SM2.pem -new -out CA.csr
openssl x509 -sm3 -req -days 30 -in CA.csr -extfile ./openssl.cnf -extensions v3_ca -signkey CA.key -out CA.crt
生成服务器端双证书:
openssl req -config ./openssl.cnf -nodes -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=server sign' -keyout SS.key -newkey ec:SM2.pem -new -out SS.csr
openssl x509 -sm3 -req -days 30 -in SS.csr -CA CA.crt -CAkey CA.key -extfile ./openssl.cnf -extensions v3_req -out SS.crt -CAcreateserial
openssl req -config ./openssl.cnf -nodes -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=server enc' -keyout SE.key -newkey ec:SM2.pem -new -out SE.csr
openssl x509 -sm3 -req -days 30 -in SE.csr -CA CA.crt -CAkey CA.key -extfile ./openssl.cnf -extensions v3enc_req -out SE.crt -CAcreateserial
生成客户端双证书:
openssl req -config ./openssl.cnf -nodes -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=client sign' -keyout CS.key -newkey ec:SM2.pem -new -out CS.csr
openssl x509 -sm3 -req -days 30 -in CS.csr -CA CA.crt -CAkey CA.key -extfile ./openssl.cnf -extensions v3_req -out CS.crt -CAcreateserial
openssl req -config ./openssl.cnf -nodes -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=client enc' -keyout CE.key -newkey ec:SM2.pem -new -out CE.csr
openssl x509 -sm3 -req -days 30 -in CE.csr -CA CA.crt -CAkey CA.key -extfile ./openssl.cnf -extensions v3enc_req -out CE.crt -CAcreateserial
验证 TLCP 连接
服务器端
openssl s_server -verify 5 -accept 4433 -cert SS.crt -key SS.key -dcert SE.crt -dkey SE.key -CAfile CA.crt
客户端:
openssl s_client -verify 5 -connect 127.0.0.1:4433 -cert CS.crt -key CS.key -dcert CE.crt -dkey CE.key -CAfile CA.crt -tlcp
存在的问题
讨论问题前,先插播两条最新新闻:
微软在 9 月初宣布,自 2023 年 9 月发布的 Windows 所有新版本,包括 Windows Insider 预览版已经确认不再支持 TLSv1.0 和 v1.1。这两者早在 2020 年就已经被业界弃用。
8 月 23 日至 24 日,微软撤销了多个拥有 30 年信任期的 DigiCert 根证书,结果 27 号又火速恢复了。然而离谱的是,到了 28 号 CloudFlare 就宣布弃用 DigiCert 所有证书,今年 10 月 25 日后所有 DigiCert 证书将无法续命。
一个独立的 SSL 系统需要浏览器、服务器、CA 三个部分。
国密 SSL 抄袭参考了已经过时的 TLSv1.1 协议,却竟然还和 TLSv1.1 不兼容,导致主流浏览器不支持、主流服务器不支持、主流 CA 系统不支持,想添加支持就不得不对源码动刀。我很难不怀疑国密 SSL 是为了彰显某国的与众不同,却丝毫没有考虑给实际应用带来的困难。
具体来讲,有下面几个问题:
- 使用了特殊的 TLS 版本号,浏览器不认识
- 使用了国密密码套件,主流浏览器和服务器不支持
- 使用了双证书,主流浏览器和服务器不支持
- 当前国密 CA 安全性堪忧。授权信息访问 (AIA) 信息不全,导致浏览器无法验证根证书;且国密证书透明制度没有标准,导致安全问题
虽然国密 SSL 问题多多,但正如前文提到的 Digicert,国际上很多知名 CA 也都是草台班子;而国密 SSL 有政府支持推动,发展起来应该也不会差。
Comments