253 lines
6.4 KiB
Go
253 lines
6.4 KiB
Go
package utils
|
||
|
||
import (
|
||
"crypto/tls"
|
||
"crypto/x509"
|
||
"encoding/pem"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
|
||
conf "github.com/tx7do/kratos-bootstrap/api/gen/go/conf/v1"
|
||
)
|
||
|
||
// 双向验证:server端提供证书(cert和key),还必须配置cerfiles即CA根证书,因为需要验证client端提供的证书。另外client也端必须提供一样的内容,即client端的证书(cert/key)以供server端验证,并且提供CA根证书验证server端提供的证书。
|
||
// 单向验证:server端提供证书(cert和key),不需要配置certfiles即CA根证书,而在客户端必须提供CA根证书,用来验证server端的证书是否有效。另外client端也不需要自己的证书,因为它不需要想server端提供验证。
|
||
|
||
// LoadServerTlsConfigFile 创建服务端TLS证书认证配置
|
||
// keyFile 服务端私钥文件路径,必须提供
|
||
// certFile 服务端证书文件路径,必须提供
|
||
// caFile CA根证书,如果提供则为双向认证,否则为单向认证
|
||
// insecureSkipVerify 用来控制客户端是否证书和服务器主机名。如果设置为true,则不会校验证书以及证书中的主机名和服务器主机名是否一致。
|
||
func LoadServerTlsConfigFile(keyFile, certFile, caFile string, insecureSkipVerify bool) (*tls.Config, error) {
|
||
if keyFile == "" || certFile == "" {
|
||
return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", keyFile, certFile)
|
||
}
|
||
|
||
var cfg tls.Config
|
||
cfg.InsecureSkipVerify = insecureSkipVerify
|
||
//cfg.ServerName = "host.docker.internal"
|
||
//cfg.MinVersion = tls.VersionTLS13
|
||
|
||
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||
if err != nil {
|
||
log.Println("tls.LoadX509KeyPair error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.Certificates = []tls.Certificate{tlsCert}
|
||
|
||
if caFile != "" {
|
||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||
cp, err := newCertPoolWithCaFile(caFile)
|
||
if err != nil {
|
||
log.Fatalln("read cert file error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.RootCAs = cp
|
||
cfg.ClientCAs = cp
|
||
} else {
|
||
cfg.ClientAuth = tls.NoClientCert
|
||
}
|
||
|
||
return &cfg, nil
|
||
}
|
||
|
||
func LoadServerTlsConfigString(keyPEMBlock, certPEMBlock, caPEMBlock []byte, insecureSkipVerify bool) (*tls.Config, error) {
|
||
if len(keyPEMBlock) == 0 || len(certPEMBlock) == 0 {
|
||
return nil, fmt.Errorf("KeyPEMBlock and CertPEMBlock must both be present[key: %v, cert: %v]", keyPEMBlock, certPEMBlock)
|
||
}
|
||
|
||
var cfg tls.Config
|
||
cfg.InsecureSkipVerify = insecureSkipVerify
|
||
//cfg.ServerName = "host.docker.internal"
|
||
//cfg.MinVersion = tls.VersionTLS13
|
||
|
||
tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||
if err != nil {
|
||
log.Println("tls.X509KeyPair error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.Certificates = []tls.Certificate{tlsCert}
|
||
|
||
if len(caPEMBlock) != 0 {
|
||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||
cp, err := newCertPool(caPEMBlock)
|
||
if err != nil {
|
||
log.Fatalln("read cert PEM error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.RootCAs = cp
|
||
cfg.ClientCAs = cp
|
||
} else {
|
||
cfg.ClientAuth = tls.NoClientCert
|
||
}
|
||
|
||
return &cfg, nil
|
||
}
|
||
|
||
func LoadServerTlsConfig(cfg *conf.TLS) (*tls.Config, error) {
|
||
if cfg == nil {
|
||
return nil, nil
|
||
}
|
||
|
||
var tlsCfg *tls.Config
|
||
var err error
|
||
|
||
if cfg.File != nil {
|
||
if tlsCfg, err = LoadServerTlsConfigFile(
|
||
cfg.File.GetKeyPath(),
|
||
cfg.File.GetCertPath(),
|
||
cfg.File.GetCaPath(),
|
||
cfg.InsecureSkipVerify,
|
||
); err != nil {
|
||
return nil, err
|
||
}
|
||
} else if cfg.Config != nil {
|
||
if tlsCfg, err = LoadServerTlsConfigString(
|
||
cfg.Config.GetKeyPem(),
|
||
cfg.Config.GetCertPem(),
|
||
cfg.Config.GetCaPem(),
|
||
cfg.InsecureSkipVerify,
|
||
); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
return tlsCfg, err
|
||
}
|
||
|
||
// LoadClientTlsConfigFile 创建客户端端TLS证书认证配置
|
||
// keyFile 客户端私钥文件路径
|
||
// certFile 客户端证书文件路径
|
||
// caFile CA根证书
|
||
func LoadClientTlsConfigFile(keyFile, certFile, caFile string) (*tls.Config, error) {
|
||
var cfg tls.Config
|
||
//cfg.InsecureSkipVerify = info.InsecureSkipVerify
|
||
//cfg.ServerName = "host.docker.internal"
|
||
//cfg.MinVersion = tls.VersionTLS13
|
||
|
||
if keyFile == "" || certFile == "" {
|
||
return &cfg, nil
|
||
}
|
||
|
||
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||
if err != nil {
|
||
log.Fatalln("read pair file error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.Certificates = []tls.Certificate{tlsCert}
|
||
|
||
if caFile != "" {
|
||
cp, err := newCertPoolWithCaFile(caFile)
|
||
if err != nil {
|
||
log.Fatalln("read cert file error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.RootCAs = cp
|
||
}
|
||
|
||
return &cfg, nil
|
||
}
|
||
|
||
func LoadClientTlsConfigString(keyPEMBlock, certPEMBlock, caPEMBlock []byte) (*tls.Config, error) {
|
||
if len(keyPEMBlock) == 0 || len(certPEMBlock) == 0 {
|
||
return nil, fmt.Errorf("KeyPEMBlock and CertPEMBlock must both be present[key: %v, cert: %v]", keyPEMBlock, certPEMBlock)
|
||
}
|
||
|
||
var cfg tls.Config
|
||
//cfg.InsecureSkipVerify = info.InsecureSkipVerify
|
||
//cfg.ServerName = "host.docker.internal"
|
||
//cfg.MinVersion = tls.VersionTLS13
|
||
|
||
tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||
if err != nil {
|
||
log.Fatalln("read pair PEM error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.Certificates = []tls.Certificate{tlsCert}
|
||
|
||
if len(caPEMBlock) != 0 {
|
||
cp, err := newCertPool(caPEMBlock)
|
||
if err != nil {
|
||
log.Fatalln("read cert PEM error:", err)
|
||
return nil, err
|
||
}
|
||
|
||
cfg.RootCAs = cp
|
||
}
|
||
|
||
return &cfg, nil
|
||
}
|
||
|
||
func LoadClientTlsConfig(cfg *conf.TLS) (*tls.Config, error) {
|
||
if cfg == nil {
|
||
return nil, nil
|
||
}
|
||
|
||
var tlsCfg *tls.Config
|
||
var err error
|
||
|
||
if cfg.File != nil {
|
||
if tlsCfg, err = LoadClientTlsConfigFile(
|
||
cfg.File.GetKeyPath(),
|
||
cfg.File.GetCertPath(),
|
||
cfg.File.GetCaPath(),
|
||
); err != nil {
|
||
return nil, err
|
||
}
|
||
} else if cfg.Config != nil {
|
||
if tlsCfg, err = LoadClientTlsConfigString(
|
||
cfg.Config.GetKeyPem(),
|
||
cfg.Config.GetCertPem(),
|
||
cfg.Config.GetCaPem(),
|
||
); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
return tlsCfg, err
|
||
}
|
||
|
||
// newCertPool creates x509 certPool with provided CA file
|
||
func newCertPoolWithCaFile(caFile string) (*x509.CertPool, error) {
|
||
pemByte, err := os.ReadFile(caFile)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return newCertPool(pemByte)
|
||
}
|
||
|
||
func newCertPool(caPEMBlock []byte) (*x509.CertPool, error) {
|
||
certPool := x509.NewCertPool()
|
||
|
||
var block *pem.Block
|
||
var cert *x509.Certificate
|
||
var err error
|
||
for {
|
||
block, caPEMBlock = pem.Decode(caPEMBlock)
|
||
if block == nil {
|
||
return certPool, nil
|
||
}
|
||
|
||
cert, err = x509.ParseCertificate(block.Bytes)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
certPool.AddCert(cert)
|
||
}
|
||
|
||
//if !certPool.AppendCertsFromPEM(caPEMBlock) {
|
||
// return nil, fmt.Errorf("can't add CA cert")
|
||
//}
|
||
//return certPool, nil
|
||
}
|