Skip to content

Commit

Permalink
实现SNI扩展客户端和服务端的应用
Browse files Browse the repository at this point in the history
实现了证书请求扩展的服务端客户端的应用
客户端Hello携带椭圆曲线ID
客户端在使用ECC系列套件时Hello消息携带支持的签名算法为SM2WithSM3
  • Loading branch information
Trisia committed Jan 9, 2025
1 parent bb5a4c1 commit 0c4e790
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 6 deletions.
11 changes: 8 additions & 3 deletions tlcp/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,7 @@ type ClientHelloInfo struct {
// CipherSuites 客户端支持的密码套件ID列表
CipherSuites []uint16

// ServerName indicates the name of the server requested by the client
// in order to support virtual hosting. ServerName is only set if the
// client is using SNI (see RFC 4366, Section 3.1).
// ServerName 客户端扩展中SNI指定的服务端名称,可以用于实现虚拟机主机。
ServerName string

// SupportedVersions 客户端支持的TLCP版本,目前只有 0x0101
Expand Down Expand Up @@ -414,6 +412,9 @@ type Config struct {
// MaxVersion 最高支持的TLCP协议版本,目前TLCP只有一个版本
MaxVersion uint16

// CurvePreferences 椭圆曲线ID列表,用于指定客户端和服务端支持的椭圆曲线
CurvePreferences []CurveID

// DynamicRecordSizingDisabled disables adaptive sizing of TLS records.
// When true, the largest possible TLS record size is always used. When
// false, the size of TLS records may be adjusted in an attempt to
Expand Down Expand Up @@ -464,6 +465,7 @@ func (c *Config) Clone() *Config {
SessionCache: c.SessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
OnAlert: c.OnAlert,
EnableDebug: c.EnableDebug,
Expand Down Expand Up @@ -630,6 +632,9 @@ type Certificate struct {
// 加密密钥对需要实现 crypto.Decrypter 接口
PrivateKey crypto.PrivateKey

// OCSPStaple 包含一个可选的OCSP响应,该响应将提供给含OCSP请求扩展的客户端
OCSPStaple []byte

// Leaf 握手x509证书对象,默认为空
//
// 可以通过 smx509.ParseCertificate 解析 Certificate.Certificate 中的第一个元素解析设置,
Expand Down
2 changes: 1 addition & 1 deletion tlcp/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type Conn struct {
peerCertificates []*x509.Certificate // 对端数字证书列表
// verifiedChains 用于验证对端证书的根证书链
verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
// serverName 由客户端Hello消息SNI扩展中指定的服务器名(域名)
serverName string

// closeNotifyErr is any error from sending the alertCloseNotify record.
Expand Down
44 changes: 43 additions & 1 deletion tlcp/handshake_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"errors"
"fmt"
"hash"
"net"
"strings"
"sync/atomic"
"time"

Expand Down Expand Up @@ -56,6 +58,15 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, error) {
vers: clientHelloVersion,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
serverName: hostnameInSNI(config.ServerName),
}

// 若用户指定了椭圆曲线偏好,则使用用户指定的椭圆曲线
if config.CurvePreferences != nil {
hello.supportedCurves = config.CurvePreferences
} else {
// 未指定时默认使用SM2
hello.supportedCurves = []CurveID{CurveSM2}
}

hasAuthKeyPair := false
Expand All @@ -77,12 +88,23 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, error) {
if suite == nil {
continue
}
// SM2 ECDHE 必须要求客户端具有认证密钥对(需要同时有签名密钥对吗?)
// SM2 ECDHE 必须要求客户端具有认证密钥对
if (suiteId == ECDHE_SM4_GCM_SM3 || suiteId == ECDHE_SM4_CBC_SM3) && !(hasAuthKeyPair && hasEncKeyPair) {
continue
}
hello.cipherSuites = append(hello.cipherSuites, suiteId)
}
// GM/T0024-2023 A.5 Signature Algorithms 签名算法
// 客户端在使用商用密码算法进行协商时,应发送 Signature Algorithms 扩展,以指定 HashAlgorithm 为SM3 和 SignatureAlgorithm 为 SM2。
for _, sigAlg := range hello.cipherSuites {
if sigAlg == ECDHE_SM4_GCM_SM3 ||
sigAlg == ECDHE_SM4_CBC_SM3 ||
sigAlg == ECC_SM4_CBC_SM3 ||
sigAlg == ECC_SM4_GCM_SM3 {
hello.supportedSignatureAlgorithms = []SignatureScheme{SM2WithSM3}
break
}
}

// 生成客户端随机数
var err error
Expand Down Expand Up @@ -668,3 +690,23 @@ func (c *Conn) getClientKECertificate(cri *CertificateRequestInfo) (*Certificate
// No acceptable certificate found. Don't send a certificate.
return nil, errNoCertificates
}

// hostnameInSNI converts name into an appropriate hostname for SNI.
// Literal IP addresses and absolute FQDNs are not permitted as SNI values.
// See RFC 6066, Section 3.
func hostnameInSNI(name string) string {
host := name
if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
host = host[1 : len(host)-1]
}
if i := strings.LastIndex(host, "%"); i > 0 {
host = host[:i]
}
if net.ParseIP(host) != nil {
return ""
}
for len(name) > 0 && name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
return name
}
3 changes: 2 additions & 1 deletion tlcp/handshake_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ type serverHelloMsg struct {

// GM/T 0024-2023 扩展字段
ocspStapling bool // 证书状态请求
ocspResponse []byte // OCSP应答内容DER ocspStapling 为true时有效
ocspResponse []byte // OCSP应答内容DER,由于GM/T 0024-2023 中没有定义 CertificateStatus 类型握手消息,所以只能通过扩展字段来传递 OCSP 响应。
alpnProtocol string // 应用层协议
serverNameAck bool // 服务器名称确认,若客户端发送了SNI扩展,服务端找到了对应的证书,则返回该扩展,内容为空
}
Expand All @@ -665,6 +665,7 @@ func (m *serverHelloMsg) marshal() ([]byte, error) {
exts.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint8(1) // status_type = ocsp
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
// !!! 由于GM/T 0024-2023 中没有定义 CertificateStatus 类型握手消息,所以只能通过扩展字段来传递 OCSP 响应。
b.AddBytes(m.ocspResponse)
})
})
Expand Down
10 changes: 10 additions & 0 deletions tlcp/handshake_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ func (hs *serverHandshakeState) processClientHello() error {
}

hs.hello.compressionMethod = compressionNone
if len(hs.clientHello.serverName) > 0 {
c.serverName = hs.clientHello.serverName
}

// 选择服务端签名证书
helloInfo := clientHelloInfo(hs.ctx, c, hs.clientHello)
Expand Down Expand Up @@ -362,6 +365,12 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
func (hs *serverHandshakeState) doFullHandshake() error {
c := hs.c

if hs.clientHello.ocspStapling && len(hs.sigCert.OCSPStaple) > 0 {
// !!! 由于GM/T 0024-2023 中没有定义 CertificateStatus 类型握手消息,所以只能通过扩展字段来传递 OCSP 响应。
hs.hello.ocspStapling = true
hs.hello.ocspResponse = hs.sigCert.OCSPStaple
}

hs.hello.cipherSuite = hs.suite.id
hs.hello.sessionId = make([]byte, 32)
// 服务端产生一个新的会话标识,用来建立一个新的会话。
Expand Down Expand Up @@ -750,6 +759,7 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg)
supportedVers := supportedVersionsFromMax(clientHello.vers)
return &ClientHelloInfo{
CipherSuites: clientHello.cipherSuites,
ServerName: c.serverName,
SupportedVersions: supportedVers,
Conn: c.conn,
config: c.config,
Expand Down

0 comments on commit 0c4e790

Please sign in to comment.