package
2.0.0+incompatible
Repository: https://github.com/zhongshuwen/gmsm.git
Documentation: pkg.go.dev

# README

TLS/GMSSL 服务端协议自适应

目录结构说明:

├─certs        // 证书以及密钥
├─websvr_test   //HTTP服务端/客户端测试Demo
└─websvr          //协议自适应实现

服务端 GMSSL/TLS 工作逻辑

autoswitchlogic

通过配置gmtls.Config 对象提供自动切换相关的配置,创建gmtls.Conn

在对gmtls.ConnRead/Wirte时将会触发握手行为HandShake

HandShake会根据用户配置参数,判断需要使用 GMSSL、TLS、GMSSL/TLS 三种工作模式中的哪一种, 然后进入到相应的工作模式中运行。

  • TLS工作模式:
    • 运行serverHandshake 进入TLS握手。
    • 创建TLS握手上下文serverHandshakeState
    • 读取并处理 来自于客户端的ClientHello 消息。
    • 进入 TLS握手流程。
  • GMSSL工作模式:
    • 运行serverHandshakeGM 进入GMSSL握手。
    • 创建TLS握手上下文serverHandshakeStateGM
    • 读取并处理 来自于客户端的ClientHello 消息。
    • 进入 GMSSL握手流程。
  • GMSSL/TLS工作模式:
    • 运行serverHandshakeAutoSwitch 进入自动切换的握手模式。
    • 读取来自于客户端的ClientHello 消息。
    • 分析处理ClientHello,根据客户端协议版本。
    • 根据协议版本,选择使用具体握手方式:
      • GMSSL: 创建上下文serverHandshakeStateGM,进入GMSSL握手流程。
      • TLS: 创建上下文serverHandshakeState,进入TLS握手流程。

在GMSSL/TLS模式的服务端运行过程中,如何根据客户端版本选择需要使用的证书以及密钥?

自动切换模式,同时需要为服务端提供2份证书与密钥对(一份用于标准的TLS、一份用于GMSSL), 在运行过程需要使用到gmtls.Config#GetCertificate方法来根据客户端的版本选择出合适的 证书密钥对,即在客户端版本是GMSSL的时候返回SM2签名证书密钥对;在客户端版本是标准的TLS时 返还RSA/ECC的证书密钥对,以次来动态适应不同客户端的连接需求。 针对于GMSSL特殊的双证书需求,特别为gmtls.Config增加了一个方法gmtls.Config#GetKECertificate 通过该方法来提供GMSSL密钥交换过程中使用密钥对。

更多细节实现见: auto_handshake_server

GMSSL/TLS 自动切换模式

快速开始:

  1. 准备 RSA、SM2签名、SM2加密,证书以及密钥对。
  2. 调用gmtls.NewBasicAutoSwitchConfig构造基础的配置对象。
  3. Use it.
func main() {
	config, err := gmtls.NewBasicAutoSwitchConfig(&sigCert, &encCert, &rsaKeypair)
	if err != nil {
		panic(err)
	}

	ln, err := gmtls.Listen("tcp", ":443", config)
	if err != nil {
		panic(err)
	}
	defer ln.Close()

	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		fmt.Fprintf(writer, "hello\n")
	})
	err = http.Serve(ln, nil)
	if err != nil {
		panic(err)
	}
}	

详细服务端的配置流程如下:

  1. 准备:
    • SM2签名密钥对、证书:sigCert
    • SM2加密密钥对、证书:encCert
    • RSA/ECC加密密钥对、证书:rsaKeypair
  2. 创建一个实现gmtls.Config#GetCertificate方法签名的方法,方法需要根据支持的签名类型:
    • 含有GMSSL版本:返回SM2签名证书密钥对(sigCert)。
    • 不含有GMSSL版本:返回RSA签名证书密钥对(rsaKeypair)。
  3. 创建一个实现gmtls.Config#GetKECertificate方法签名的方法,固定返回SM2加密证书密钥对(encCert)。
  4. 创建GMSupport并启用,自动切换模式。
  5. 创建gmtls.Config对象,接下就可以启动服务端实现自动切换功能。
fncGetSignCertKeypair := func(info *gmtls.ClientHelloInfo) (*gmtls.Certificate, error) {
    gmFlag := false
    // 检查支持协议中是否包含GMSSL
    for _, v := range info.SupportedVersions {
        if v == gmtls.VersionGMSSL {
            gmFlag = true
            break
        }
    }
    if gmFlag {
        return &sigCert, nil
    } else {
        return &rsaKeypair, nil
    }
}

fncGetEncCertKeypair := func(info *gmtls.ClientHelloInfo) (*gmtls.Certificate, error) {
    return &encCert, nil
}
support := gmtls.NewGMSupport()
support.EnableMixMode()
config := &gmtls.Config{
    GMSupport:        support,
    GetCertificate:   fncGetSignCertKeypair,
    GetKECertificate: fncGetEncCertKeypair,
}

更多细节请参考: HTTP over GMTLS/TLS Server Demo

TCLP 双向身份认证

服务端配置

  1. 设置启用国密TLCP支持。
  2. 配置服务端签名证书密钥和加密证书密钥。
  3. 设置服务端开启双向身份认证,并要求验证客户端证书。
  4. 配置根证书链,用于验证客户端证书。
config := &gmtls.Config{
    GMSupport:    gmtls.NewGMSupport(),
    Certificates: []gmtls.Certificate{sigCert, encCert},
    ClientAuth:   gmtls.RequireAndVerifyClientCert,
    ClientCAs:    certPool,
}

更多细节请参考:

客户端配置

  1. 设置启用国密TLCP支持。
  2. 配置双向身份认证的客户端方的签名证书和密钥对。
  3. 提供验证服务端证书的根证书链。
  4. 设置需要进行安全校验。

例如:

config := &gmtls.Config{
    GMSupport:          gmtls.NewGMSupport(),
    RootCAs:            certPool,
    Certificates:       []gmtls.Certificate{authKeypair},
    InsecureSkipVerify: false,
}

更多细节请参考:

国密HTTPS客户端

为了简化HTTPS客户端的构造gmtls包提供下面构造方法:

  • 创建单向身份认证HTTPS客户端:gmtls.NewHTTPSClient(*x509.CertPool)
  • 创建双向身份认证HTTPS客户端:gmtls.NewAuthHTTPSClient(*x509.CertPool, *gmtls.Certificate)
  • 创建定制化的TLS连接的HTTPS客户端:gmtls.NewCustomHTTPSClient(*gmtls.Config)

HTTPS 单向身份认证

单向身份认证客户端,只只验证服务器证书有效性,服务端不对客户端进行身份认证。

该模式下进行国密HTTPS的调用你需要:

  1. 提供根证书链。
  2. 构造HTTP客户端。
  3. 调用API访问HTTPS。
package main

import (
	"github.com/zhongshuwen/gmsm/gmtls"
	"github.com/zhongshuwen/gmsm/x509"
	"io/ioutil"
)

func main() {
	// 1. 提供根证书链
	certPool := x509.NewCertPool()
	cacert, err := ioutil.ReadFile("websvr/certs/SM2_CA.cer")
	if err != nil {
		panic(err)
	}
	certPool.AppendCertsFromPEM(cacert)
	// 2. 构造HTTP客户端
	httpClient := gmtls.NewHTTPSClient(certPool)
	// 3. 调用API访问HTTPS
	response, err := httpClient.Get("https://localhost:443")
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	// 使用 response 做你需要的事情...
}

更多细节见 gmsm/http_client_test.go#TestNewHTTPSClient2

HTTPS 双向身份认证

双向身份认证,在服务端开启了对客户端的身份认证情况下,国密SSL通行就需要进行双向身份认证。

该模式下进行国密HTTPS的调用你需要:

  1. 提供根证书链。
  2. 提供客户端认证证书、密钥对。
  3. 构造HTTP客户端。
  4. 调用API访问HTTPS。
package main

import (
	"github.com/zhongshuwen/gmsm/gmtls"
	"github.com/zhongshuwen/gmsm/x509"
	"io/ioutil"
)

func main() {
	// 1. 提供根证书链
	certPool := x509.NewCertPool()
	cacert, err := ioutil.ReadFile("websvr/certs/SM2_CA.cer")
	if err != nil {
		panic(err)
	}
	certPool.AppendCertsFromPEM(cacert)
	// 2. 提供客户端认证证书、密钥对。
	clientAuthCert, err := gmtls.LoadX509KeyPair("websvr/certs/sm2_auth_cert.cer", "websvr/certs/sm2_auth_key.pem")
	// 3. 构造HTTP客户端。
	httpClient := gmtls.NewAuthHTTPSClient(certPool, &clientAuthCert)
	// 4. 调用API访问HTTPS。
	response, err := httpClient.Get("https://localhost:443")
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	// 使用 response 做你需要的事情...
}

更多细节见 gmsm/http_client_test.go#TestSimpleNewAuthHTTPSClient

TLCP GCM模式

《GBT 38636-2020 信息安全技术 传输层密码协议(TLCP)》协议中增加了GCM可鉴别加密模式相关的系列密码套件。

ECC_SM4_GCM_SM3 密码套件为例,该套件使用SM4算法GCM可鉴别加密模式,替换了“SM4 CBC模式 + SM3 HMAC”,其余保持不变。

GCM模式下:

  • 密文数据结构和生成规则详见: 《GBT 38636-2020》 6.3.3.4.4 认证加密算法的数据处理
  • 实现随机数实现细节见:RFC 5116 (AES_GCM 参考 RFC 5288)

如何使用?

  • 目前客户端和服务端均支持 ECC_SM4_GCM_SM3ECC_SM4_CBC_SM3 密码套件。
  • 服务端:无需而外配置。
  • 客户端:目前客户单默认使用ECC_SM4_CBC_SM3密码套件,需要手动配置才可以使用 ECC_SM4_GCM_SM3套件。

目前支持TLCP密码套件:

  • ECC_SM4_GCM_SM3
  • ECC_SM4_CBC_SM3
  • ECDHE_SM4_CBC_SM3
  • ECDHE_SM4_GCM_SM3

客户端 GCM配置

以单向身份认证举例,只需要在连接配置中增加响应的算法,将GCM模式套件放在数组较前面的位置就可以。

示例如下:

package main

import (
	"github.com/zhongshuwen/gmsm/gmtls"
	"github.com/zhongshuwen/gmsm/x509"
	"io/ioutil"
	"log"
)

func main() {
	// 信任的根证书
	certPool := x509.NewCertPool()
	cacert, err := ioutil.ReadFile("root.cer")
	if err != nil {
		log.Fatal(err)
	}
	certPool.AppendCertsFromPEM(cacert)
	cert, err := gmtls.LoadX509KeyPair("sm2_cli.cer", "sm2_cli.pem")

	config := &gmtls.Config{
		GMSupport:    &gmtls.GMSupport{},
		RootCAs:      certPool,
		Certificates: []gmtls.Certificate{cert},
		// 设置GCM模式套件放在前面
		CipherSuites: []uint16{gmtls.GMTLS_ECC_SM4_GCM_SM3, gmtls.GMTLS_ECC_SM4_CBC_SM3},
	}

	conn, err := gmtls.Dial("tcp", "localhost:50052", config)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 对 conn 读取或写入
}

关于 TLCP AEAD随机数

该套件采用 显式 和 隐式 随机数构造,AEAD随机数(Nonce),总长12字节,4字节隐式随机数、8字节显式部分。

      +---------------------+----------------------------------+
      |      Fixed(4byte)   |          Counter(8byte)          |
      +---------------------+----------------------------------+
      <----  隐式随机数  ---> <------------ 显式部分 ------------>
  • 隐式随机数为: 工作密钥中的 客户端写IV(client_write_IV ) 或 服务端写IV(server_write_IV)。
  • 显式部分为: 数据包序号(seq_num),也就是GenericAEADCipher.nonce_explicit字段。

详见 RFC 5116 3.2. Recommended Nonce Formation

AEAD SM4 与 AEAD AES 128 实现一致。

# Constants

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author