Environment
- GmSSL: 3.2.0, built from source
- Client command:
gmssl tlcp_client
- Server: Kona JDK/KonaSSL based TLCP HTTPS server, Jetty 12
- Protocol/cipher suite:
TLCPv1.1, TLS_ECC_SM4_GCM_SM3
- Server certificate chain: TLCP signing certificate, encryption certificate, CA certificate
Reproduce
gmssl tlcp_client \
-host 127.0.0.1 \
-port 18081 \
-server_name localhost \
-cacert ca.pem \
-cipher_suite TLS_ECC_SM4_GCM_SM3 \
-get /ping \
-verbose
The server sends the TLCP server handshake flight in one TLS record, containing multiple handshake messages:
ServerHello
Certificate
ServerKeyExchange
ServerHelloDone
Actual behavior
The client fails during the handshake. From debugging, tls_record_get_handshake() appears to expect one handshake message to occupy the whole record. When a record contains more than one handshake message, the remaining bytes cause the following handshake state to fail with unexpected_message.
After locally working around this by splitting handshake messages from the same record, certificate verification continues but fails on BasicConstraints parsing/checking:
src/x509_ext.c:3019:x509_exts_check()
src/x509_ext.c:2141:x509_basic_constraints_from_der()
src/x509_cer.c:1882:x509_cert_check()
src/x509_cer.c:2166:x509_certs_verify_tlcp()
src/tls_vrf.c:411:tls_cert_chain_verify()
src/tlcp.c:874:tlcp_recv_server_certificate()
Alert: bad_certificate (42)
The entity certificates contain an empty BasicConstraints sequence, which should mean cA = false and no pathLenConstraint:
Extension
extnID: BasicConstraints (2.5.29.19)
BasicConstraints
Expected behavior
- The TLCP client should accept valid TLS/TLCP record framing where one record may contain multiple handshake messages. TLS record boundaries should not be assumed to match handshake message boundaries.
BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER OPTIONAL }; an empty sequence should be accepted as an end-entity certificate with cA = false and absent pathLenConstraint.
Local verification
I made a local test patch with two changes:
- In the TLCP client receive path, buffer and split multiple handshake messages contained in one record before processing the next handshake state.
- In
x509_basic_constraints_from_der(), treat an empty BasicConstraints sequence as cA = false and pathLenConstraint = absent.
With this local patch, the same command succeeds:
Connection established
Protocol: TLCP
CipherSuite: TLS_ECC_SM4_GCM_SM3
Certificate.subject: commonName: localhost
Certificate.issuer: commonName: timecho-gm-ca
HTTP/1.1 200 OK
{"code":200,"message":"SUCCESS_STATUS"}
There may also be a stricter compatibility question around CA certificates whose BasicConstraints is not marked critical. If that strictness is intentional, please ignore that part; the two issues above are the main interoperability blockers I observed.
中文版
环境
- GmSSL:3.2.0,从源码编译
- 客户端命令:
gmssl tlcp_client
- 服务端:基于 Kona JDK/KonaSSL 的 TLCP HTTPS 服务端,Jetty 12
- 协议和密码套件:
TLCPv1.1,TLS_ECC_SM4_GCM_SM3
- 服务端证书链:TLCP 签名证书、加密证书、CA 证书
复现方式
gmssl tlcp_client \
-host 127.0.0.1 \
-port 18081 \
-server_name localhost \
-cacert ca.pem \
-cipher_suite TLS_ECC_SM4_GCM_SM3 \
-get /ping \
-verbose
服务端会在同一个 TLS record 中发送 TLCP 服务端握手消息,里面连续包含多个 handshake message:
ServerHello
Certificate
ServerKeyExchange
ServerHelloDone
实际行为
客户端在握手过程中失败。通过调试看,tls_record_get_handshake() 似乎假设一个 handshake message 会占满整个 record。当一个 record 中包含多个 handshake message 时,剩余字节会导致后续握手状态失败,并报 unexpected_message。
本地临时修改 TLCP client 接收逻辑,让它可以从同一个 record 中拆分并缓存多个 handshake message 后,握手可以继续,但随后会在证书校验阶段因为 BasicConstraints 解析/检查失败而发送 bad_certificate:
src/x509_ext.c:3019:x509_exts_check()
src/x509_ext.c:2141:x509_basic_constraints_from_der()
src/x509_cer.c:1882:x509_cert_check()
src/x509_cer.c:2166:x509_certs_verify_tlcp()
src/tls_vrf.c:411:tls_cert_chain_verify()
src/tlcp.c:874:tlcp_recv_server_certificate()
Alert: bad_certificate (42)
实体证书中的 BasicConstraints 是一个空 SEQUENCE,含义应当是 cA = false,且没有 pathLenConstraint:
Extension
extnID: BasicConstraints (2.5.29.19)
BasicConstraints
期望行为
- TLCP client 应该支持合法的 TLS/TLCP record 分帧方式:一个 record 中可以包含多个 handshake message。TLS record 边界不应被假设为 handshake message 边界。
BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER OPTIONAL };空 SEQUENCE 应该被接受,并表示实体证书 cA = false、pathLenConstraint 不存在。
本地验证
我本地做了一个测试补丁,包含两处修改:
- 在 TLCP client 接收服务端握手消息时,如果同一个 record 中包含多个 handshake message,就拆分并缓存剩余 handshake 数据,供下一个握手状态继续消费。
- 在
x509_basic_constraints_from_der() 中,把空的 BasicConstraints sequence 解析为 cA = false 和 pathLenConstraint = absent。
加上这个本地补丁后,同样的命令可以成功:
Connection established
Protocol: TLCP
CipherSuite: TLS_ECC_SM4_GCM_SM3
Certificate.subject: commonName: localhost
Certificate.issuer: commonName: timecho-gm-ca
HTTP/1.1 200 OK
{"code":200,"message":"SUCCESS_STATUS"}
另外,CA 证书中的 BasicConstraints 未标记为 critical 时,GmSSL 当前也会比较严格地拒绝。这个点如果是有意保持严格校验,可以先忽略;上面两个问题是我这次观察到的主要互通阻塞点。
Environment
gmssl tlcp_clientTLCPv1.1,TLS_ECC_SM4_GCM_SM3Reproduce
The server sends the TLCP server handshake flight in one TLS record, containing multiple handshake messages:
Actual behavior
The client fails during the handshake. From debugging,
tls_record_get_handshake()appears to expect one handshake message to occupy the whole record. When a record contains more than one handshake message, the remaining bytes cause the following handshake state to fail withunexpected_message.After locally working around this by splitting handshake messages from the same record, certificate verification continues but fails on
BasicConstraintsparsing/checking:The entity certificates contain an empty
BasicConstraintssequence, which should meancA = falseand nopathLenConstraint:Expected behavior
BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER OPTIONAL }; an empty sequence should be accepted as an end-entity certificate withcA = falseand absentpathLenConstraint.Local verification
I made a local test patch with two changes:
x509_basic_constraints_from_der(), treat an empty BasicConstraints sequence ascA = falseandpathLenConstraint = absent.With this local patch, the same command succeeds:
There may also be a stricter compatibility question around CA certificates whose
BasicConstraintsis not marked critical. If that strictness is intentional, please ignore that part; the two issues above are the main interoperability blockers I observed.中文版
环境
gmssl tlcp_clientTLCPv1.1,TLS_ECC_SM4_GCM_SM3复现方式
服务端会在同一个 TLS record 中发送 TLCP 服务端握手消息,里面连续包含多个 handshake message:
实际行为
客户端在握手过程中失败。通过调试看,
tls_record_get_handshake()似乎假设一个 handshake message 会占满整个 record。当一个 record 中包含多个 handshake message 时,剩余字节会导致后续握手状态失败,并报unexpected_message。本地临时修改 TLCP client 接收逻辑,让它可以从同一个 record 中拆分并缓存多个 handshake message 后,握手可以继续,但随后会在证书校验阶段因为
BasicConstraints解析/检查失败而发送bad_certificate:实体证书中的
BasicConstraints是一个空 SEQUENCE,含义应当是cA = false,且没有pathLenConstraint:期望行为
BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER OPTIONAL };空 SEQUENCE 应该被接受,并表示实体证书cA = false、pathLenConstraint不存在。本地验证
我本地做了一个测试补丁,包含两处修改:
x509_basic_constraints_from_der()中,把空的 BasicConstraints sequence 解析为cA = false和pathLenConstraint = absent。加上这个本地补丁后,同样的命令可以成功:
另外,CA 证书中的
BasicConstraints未标记为 critical 时,GmSSL 当前也会比较严格地拒绝。这个点如果是有意保持严格校验,可以先忽略;上面两个问题是我这次观察到的主要互通阻塞点。