funcmain(){// 从证书相关文件中读取和解析信息,得到证书公钥、密钥对certificate,err:=tls.LoadX509KeyPair(data.Path("x509/server.crt"),data.Path("x509/server.key"))iferr!=nil{log.Fatal(err)}// 创建CertPool,后续就用池里的证书来校验客户端证书有效性// 所以如果有多个客户端 可以给每个客户端使用不同的 CA 证书,来实现分别校验的目的certPool:=x509.NewCertPool()ca,err:=ioutil.ReadFile(data.Path("x509/ca.crt"))iferr!=nil{log.Fatal(err)}ifok:=certPool.AppendCertsFromPEM(ca);!ok{log.Fatal("failed to append certs")}// 构建基于 TLS 的 TransportCredentialscreds:=credentials.NewTLS(&tls.Config{// 设置证书链,允许包含一个或多个Certificates:[]tls.Certificate{certificate},// 要求必须校验客户端的证书 可以根据实际情况选用其他参数ClientAuth:tls.RequireAndVerifyClientCert,// NOTE: this is optional!// 设置根证书的集合,校验方式使用 ClientAuth 中设定的模式ClientCAs:certPool,})s:=grpc.NewServer(grpc.Creds(creds))ecpb.RegisterEchoServer(s,&ecServer{})lis,err:=net.Listen("tcp",fmt.Sprintf(":%d",*port))iferr!=nil{log.Fatalf("failed to listen: %v",err)}log.Println("Serving gRPC on 0.0.0.0"+fmt.Sprintf(":%d",*port))iferr:=s.Serve(lis);err!=nil{log.Fatalf("failed to serve: %v",err)}}
funcmain(){// 加载客户端证书certificate,err:=tls.LoadX509KeyPair(data.Path("x509/client.crt"),data.Path("x509/client.key"))iferr!=nil{log.Fatal(err)}// 构建CertPool以校验服务端证书有效性certPool:=x509.NewCertPool()ca,err:=ioutil.ReadFile(data.Path("x509/ca.crt"))iferr!=nil{log.Fatal(err)}ifok:=certPool.AppendCertsFromPEM(ca);!ok{log.Fatal("failed to append ca certs")}creds:=credentials.NewTLS(&tls.Config{Certificates:[]tls.Certificate{certificate},ServerName:"www.lixueduan.com",// NOTE: this is required!RootCAs:certPool,})conn,err:=grpc.Dial(*addr,grpc.WithTransportCredentials(creds))iferr!=nil{log.Fatalf("DialContext error:%v",err)}deferconn.Close()client:=ecpb.NewEchoClient(conn)callUnaryEcho(client,"hello world")}
4. Test
Server
1
2
lixd@17x:~/17x/projects/grpc-go-example/features/encryption/mutual-TLS/server$ go run main.go
2021/01/24 18:02:01 Serving gRPC on 0.0.0.0:50051
Client
1
2
lixd@17x:~/17x/projects/grpc-go-example/features/encryption/mutual-TLS/client$ go run main.go
UnaryEcho: hello world
一切正常,大功告成。
4. FAQ
问题
1
error:rpc error: code= Unavailable desc= connection error: desc="transport: authentication handshake failed: x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0"
由于之前使用的不是 SAN 证书,在Go版本升级到1.15后出现了该问题。
原因
Go 1.15 版本开始废弃 CommonName 并且推荐使用 SAN 证书,导致依赖 CommonName 的证书都无法使用了。