Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

支持TLS传输国密 #290

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@ module github.com/xuperchain/xupercore
go 1.14

require (
github.com/ChainSafe/go-schnorrkel v0.0.0-20200626160457-b38283118816 // indirect
github.com/aws/aws-sdk-go v1.32.4
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/dgraph-io/badger/v3 v3.2103.1
github.com/docker/go-connections v0.4.1-0.20180821093606-97c2040d34df // indirect
github.com/docker/go-units v0.4.0
github.com/emirpasic/gods v1.12.1-0.20201118132343-79df803e554c
github.com/fsouza/go-dockerclient v1.6.0
github.com/gammazero/deque v0.1.0
github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.4.3
github.com/golang/snappy v0.0.3
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2
github.com/hashicorp/golang-lru v0.5.4
github.com/hyperledger/burrow v0.30.5
Expand All @@ -33,10 +30,11 @@ require (
github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.6.2
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca
github.com/tjfoc/gmsm v1.4.1
github.com/xuperchain/crypto v0.0.0-20201028025054-4d560674bcd6
github.com/xuperchain/log15 v0.0.0-20190620081506-bc88a9198230
github.com/xuperchain/xvm v0.0.0-20210126142521-68fd016c56d7
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
google.golang.org/grpc v1.35.0
)
Expand Down
94 changes: 77 additions & 17 deletions kernel/network/p2p/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@ package p2p

import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
defaulttls "crypto/tls"
defaultx509 "crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"io/ioutil"
math_rand "math/rand"
"os"
"path/filepath"
"strings"
"sync"
"time"

tls "github.com/tjfoc/gmsm/gmtls"
"github.com/tjfoc/gmsm/gmtls/gmcredentials"
"github.com/tjfoc/gmsm/x509"

iaddr "github.com/ipfs/go-ipfs-addr"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
Expand All @@ -20,32 +27,85 @@ import (
"github.com/xuperchain/xupercore/kernel/network/config"
)

// serverName 为key,缓存 creds
var serverNameMap = make(map[string]credentials.TransportCredentials)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全局变量的读写最好加锁进行保护

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的


//修改全局变量 serverNameMap 加锁
var mu sync.Mutex

func NewTLS(path, serviceName string) (credentials.TransportCredentials, error) {

if len(serviceName) < 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

去掉 ServerName 为空的判断,标准库TLS 是允许ServerName 为空的
ServerName 为空时,标准库中 SSL 握手的证书为

  • 客户端: 根据连接时根据 dial 的 addr 判断自动推断 ServerName
  • 服务端: 不需要 ServerName 字段
    gmtls 的逻辑应该也类似,可以进一步确认

return nil, errors.New("serviceName is empty")
}

//如果缓存中有值
if creds, ok := serverNameMap[serviceName]; ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serverNameMap 对应性能等没有实质性的作用,反而增加了代码的复杂度。
TransportCredentials 的使用包括两种场景

  1. 服务端: Accept 完成 TCP 三次握手之后,调用 Server 对象上的 opts.creds 完成SSL 握手中的服务端部分,不会重复加载TransportCredentials
  2. 客户端: 底层采用 HTTP2 长链接,同一个服务端只有一个连接,不需要重复加载TransportCredentials,serverNameMap 缓存 基本处于缓存不命中的情况,对性能等没有实质性的帮助

return creds, nil
}

mu.Lock()
defer mu.Unlock()
//读取 cacert.pem 证书
bs, err := ioutil.ReadFile(filepath.Join(path, "cacert.pem"))
if err != nil {
return nil, err
}

certPool := x509.NewCertPool()
ok := certPool.AppendCertsFromPEM(bs)
if !ok {
cacert, err := ioutil.ReadFile(filepath.Join(path, "cacert.pem"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bs 和 cacert 是同样的内容,不需要重复读取

if err != nil {
return nil, err
}

certificate, err := tls.LoadX509KeyPair(filepath.Join(path, "cert.pem"), filepath.Join(path, "private.key"))
pb, _ := pem.Decode(cacert)
x509cert, err := x509.ParseCertificate(pb.Bytes)
if err != nil {
return nil, err
}

creds := credentials.NewTLS(
&tls.Config{
ServerName: serviceName,
Certificates: []tls.Certificate{certificate},
RootCAs: certPool,
ClientCAs: certPool,
ClientAuth: tls.RequireAndVerifyClientCert,
})
return creds, nil
if strings.Contains(strings.ToLower(x509cert.SignatureAlgorithm.String()), "sm") { //国密
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修改判断是否使用国密的逻辑,区分客户端和服务端
客户端通过配置项选择是否使用国密,或者通过本地证书判断支持的密码学套件
服务端支持支持加载多个证书,根据 SSL 握手时协商的结果选择对应的密码学套件
理由如下

SSL/TLS 握手区分服务端和客户端,具体流程如图所示
image

其中,ClientHello 结构中包含了客户端支持的加密套件,服务端根据自身的支持的加密套件和客户端支持的加密套件返回对应的加密套件协商结果。

ClientHello 字段参考下图
image

客户端是否使用国密可以通过以下方案判断

  1. 通过配置项来判断是否使用国密
  2. 通过判断证书类型确定是否使用国密。
    值得注意的是,在商用密码系列算法中,SM2为公钥加密, SM3 为哈希算法,SM4 为分组加密。gmtls 支持SM2,SM3,SM4 其中只有SM2 可能出现在证书的 PublicKeyAlgorithm 字段,而不必要使字符串比较。

服务端通过如下方案
服务端在收到 Client 服务端在 ClientHello 信息之后,会通过 ClientHelloHello 信息之后,会通过 ClientHello 中的 SupportedVersions 字段决定是否使用国密。

certPool := x509.NewCertPool()
ok := certPool.AppendCertsFromPEM(bs)
if !ok {
return nil, err
}
certificate, err := tls.LoadX509KeyPair(filepath.Join(path, "cert.pem"), filepath.Join(path, "private.key"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gmtls 中的 x509 证书兼容标准的 RSA x509 解析,同一个证书只需要加载和解析一次,可以参考官方示例
https://github.com/tjfoc/gmsm/blob/fdaa5ff368db5ce62bb672fc424b23f8923858dc/gmtls/websvr/websvr.go#L52

if err != nil {
return nil, err
}
creds := gmcredentials.NewTLS(
&tls.Config{
GMSupport: tls.NewGMSupport(),
ServerName: serviceName,
Certificates: []tls.Certificate{certificate, certificate},
RootCAs: certPool,
ClientCAs: certPool,
ClientAuth: tls.RequireAndVerifyClientCert,
})
serverNameMap[serviceName] = creds
return creds, nil
} else { //非国密
certPool := defaultx509.NewCertPool()
ok := certPool.AppendCertsFromPEM(bs)
if !ok {
return nil, err
}

certificate, err := defaulttls.LoadX509KeyPair(filepath.Join(path, "cert.pem"), filepath.Join(path, "private.key"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

通 70 行 comment

if err != nil {
return nil, err
}

creds := credentials.NewTLS(
&defaulttls.Config{
ServerName: serviceName,
Certificates: []defaulttls.Certificate{certificate},
RootCAs: certPool,
ClientCAs: certPool,
ClientAuth: defaulttls.RequireAndVerifyClientCert,
})
serverNameMap[serviceName] = creds
return creds, nil
}

}

// GenerateKeyPairWithPath generate xuper net key pair
Expand Down