GO GRPC 證書報錯
報錯
報錯信息
transport: authentication handshake failed: x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0
原因
是因為 go 1.15 版本開始廢棄 CommonName,因此推薦使用 SAN 證書。 如果想兼容之前的方式,需要設置環境變量 GODEBUG 為 x509ignoreCN=0。
什么是 SAN
SAN(Subject Alternative Name) 是 SSL 標准 x509 中定義的一個擴展。使用了 SAN 字段的 SSL 證書,可以擴展此證書支持的域名,使得一個證書可以支持多個不同域名的解析。
解決
1. 修改GODEBUG方式
GODEBUG="x509ignoreCN=0" go run main.go
2. 使用SAN證書
2.1 生成 CA 根證書
# Key considerations for algorithm "RSA" ≥ 2048-bit
$ openssl genrsa -out server.key 2048
# Key considerations for algorithm "ECDSA" ≥ secp384r1
# List ECDSA the supported curves (openssl ecparam -list_curves)
$ openssl ecparam -genkey -name secp384r1 -out server.key
openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
2.2 修改配置
cp /etc/ssl/openssl.cnf ./keys/
vim ./keys/openssl.cnf
……
[CA_default]
# 打開copy_extensions的注釋
copy_extensions = copy
……
[ req ]
# 將req_extensions的注釋打開
req_extensions = v3_req # The extensions to add to a certificate request
……
[ v3_req ]
# 添加subjectAltName
subjectAltName = @alt_names
# 文件末尾添加. www.p-pp.cn 和 *.p-pp.cn 代表允許的ServerName
[alt_names]
DNS.1 = www.p-pp.cn
DNS.2 = *.p-pp.cn
2.3 生成服務端證書
openssl genpkey -algorithm RSA -out hello.key
openssl req -new -nodes -key hello.key -out hello.csr -days 3650 -subj "/C=cn/ST=SH/L=SH/OU=SRE/O=TEST/CN=www.p-pp.cn" -config ./openssl.cnf -extensions v3_req
Ignoring -days; not generating a certificate
openssl x509 -req -days 3650 -in hello.csr -out hello.pem -CA server.pem -CAkey server.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
Signature ok
subject=C = cn, ST = SH, L = SH, OU = SRE, O = TEST, CN = www.p-pp.cn
Getting CA Private Key
2.4 修改代碼
Server
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"os"
pb "grpc-hello/proto/hello" // 引入編譯生成的包
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
)
const (
// Address gRPC服務地址
Address = "127.0.0.1:50052"
)
// 定義helloService並實現約定的接口
type helloService struct{}
// HelloService Hello服務
var HelloService = helloService{}
// SayHello 實現Hello服務接口
func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
grpclog.Println("收到請求grpclog")
resp := new(pb.HelloResponse)
resp.Message = fmt.Sprintf("Hello %s.", in.Name)
return resp, nil
}
func main() {
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stdout))
listen, err := net.Listen("tcp", Address)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
// TLS認證
cert, _ := tls.LoadX509KeyPair("../keys/hello.pem", "../keys/hello.key")
certPool := x509.NewCertPool()
ca, _ := ioutil.ReadFile("../keys/server.pem")
certPool.AppendCertsFromPEM(ca)
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
})
// 實例化grpc Server
s := grpc.NewServer(grpc.Creds(creds))
// 注冊HelloService
pb.RegisterHelloServer(s, HelloService)
grpclog.Infoln("Listen on " + Address + " with TSL")
s.Serve(listen)
}
Client
package main
import (
"crypto/tls"
"crypto/x509"
pb "grpc-hello/proto/hello" // 引入proto包
"io/ioutil"
"os"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
)
const (
// Address gRPC服務地址
Address = "127.0.0.1:50052"
)
func main() {
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stdout))
// TLS連接 記得把server name改成你寫的服務器地址
cert, _ := tls.LoadX509KeyPair("../keys/hello.pem", "../keys/hello.key")
certPool := x509.NewCertPool()
ca, _ := ioutil.ReadFile("../keys/server.pem")
certPool.AppendCertsFromPEM(ca)
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: "www.p-pp.cn",
RootCAs: certPool,
})
// 連接
conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds))
if err != nil {
grpclog.Fatalln(err)
}
defer conn.Close()
// 初始化客戶端
c := pb.NewHelloClient(conn)
// 調用方法
req := &pb.HelloRequest{Name: "gRPC"}
res, err := c.SayHello(context.Background(), req)
if err != nil {
grpclog.Fatalln(err)
}
grpclog.Println(res.Message)
}
參考鏈接
https://www.cnblogs.com/custer/p/13999946.html
https://www.cnblogs.com/jackluo/p/13841286.html