layout | title | date | categories | tags | comments | mathjax | copyrights |
---|---|---|---|---|---|---|---|
post |
国密SSL |
2023-09-04 00:00:00 +0800 |
密码 |
ssl tlcp tls |
true |
true |
原创 |
本文讨论 TLCP(国密 SSL)。
国密算法还不少,包括 SM1、SM2、SM3、SM4、SM7、SM9、ZUC 等等。我还查了一下,SM 的意思肯定不是那个啥,而是 ShangMi(商密)……
- SM1 是个非公开的对称密码算法,由硬件实现
- SM2 是个公钥密码算法
- SM3 是个摘要算法
- SM4 是个对称密码算法
- SM7 是个非公开的对称密码算法,用于非接触式 IC 卡等
- SM9 是个非对称密码算法
- ZUC 是个流加密算法
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 是个密码杂凑算法,介绍它具体的参数没有什么意义。详见 GB/T 32905-2016《信息安全技术SM3密码杂凑算法》
SM4 用的是分组对称加密,同样的,介绍它具体的参数也没有什么意义。详见 GB/T 32907-2016《信息安全技术SM4分组密码算法》
传输层密码协议 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)产生并由外部认证机构签发加密证书。
我们将签名密钥对与加密密钥对统称为服务端密钥 。
标准版本的 OpenSSL 是不支持 TLCP 的,我们假设已经拥有了魔改过的 OpenSSL。
编写 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
服务器端
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 有政府支持推动,发展起来应该也不会差。