Ch3nyang's blog

home

home

person

about

hive

project

collections_bookmark

mindclip

rss_feed

rss

国密SSL

calendar_month 2023-09
archive 密码
tag ssl tag tlcp tag tls

本文讨论 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

Share This Post