https双向认证 GO 1.15 以上版本解决GRPC X509 Common Name field, use SANs or temporarily enable Common Name matching
第1步:生成 CA 根证书 1 openssl genrsa -out ca.key 2048
1 2 3 4 Generating RSA private key, 2048 bit long modulus (2 primes) .............+++++ ..................................................................................................................+++++ e is 65537 (0x010001)
1 openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.' , the field will be left blank. ----- Country Name (2 letter code) [AU]:cn State or Province Name (full name) [Some-State]:fj Locality Name (eg, city) []:fz Organization Name (eg, company) [Internet Widgits Pty Ltd]:wyy Organizational Unit Name (eg, section) []:wyy Common Name (e.g. server FQDN or YOUR name) []:localhost Email Address []:
第2步:用 openssl 生成 ca 和双方 SAN 证书
https://raw.fastgit.org/openssl/openssl/master/apps/openssl.cnf
准备默认 OpenSSL 配置文件于当前目录
linux系统在 : /etc/ssl/openssl.cnf
Mac系统在: /System/Library/OpenSSL/openssl.cnf
window系统(cmder):C:/Users/<user>/Desktop/cmder/vendor/git-for-windows/usr/ssl/openssl.cnf
1:cp 目录到项目目录进行修改设置
1 cp C:/Users/<user>/Desktop/cmder/vendor/git-for-windows/usr/ssl/openssl.cnf ./
2:找到 [ CA_default ],打开 copy_extensions = copy
3:找到[ req ],打开 req_extensions = v3_req # The extensions to add to a certificate request
4:找到[ v3_req ],添加 subjectAltName = @alt_names
5:添加新的标签 [ alt_names ] , 和标签字段
1 2 3 [ alt_names ] DNS.1 = localhost DNS.2 = *.test.com
这里填入需要加入到 Subject Alternative Names 段落中的域名名称,可以写入多个。
第3步:生成服务端证书 1 openssl genpkey -algorithm RSA -out server.key
1 2 ........................................................................................+++++ .......................................+++++
1 openssl req -new -nodes -key server.key -out server.csr -days 3650 -subj "/C=cn/OU=wyy/O=wyy/CN=localhost" -config ./openssl.cnf -extensions v3_req
1 Ignoring -days; not generating a certificate
1 openssl x509 -req -days 3650 -in server.csr -out server.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
1 2 3 Signature ok subject=C = cn, OU = custer, O = custer, CN = localhost Getting CA Private Key
server.csr是上面生成的证书请求文件。ca.pem/ca.key是CA证书文件和key,用来对server.csr进行签名认证。这两个文件在之前生成的。
第4步:生成客户端证书 1 openssl genpkey -algorithm RSA -out client.key
1 2 ........+++++ ...........+++++
1 openssl req -new -nodes -key client.key -out client.csr -days 3650 -subj "/C=cn/OU=wyy/O=wyy/CN=localhost" -config ./openssl.cnf -extensions v3_req
1 Ignoring -days; not generating a certificate
1 openssl x509 -req -days 3650 -in client.csr -out client.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
1 2 3 Signature ok subject=C = cn, OU = custer, O = custer, CN = localhost Getting CA Private Key
现在 Go 1.15 以上版本的 GRPC 通信,这样就完成了使用自签CA、Server、Client证书和双向认证
示例 rpc 服务端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { cert, _ := tls.LoadX509KeyPair("cert/server.pem" , "cert/server.key" ) certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("cert/ca.pem" ) certPool.AppendCertsFromPEM(ca) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: certPool, }) rpcServer := grpc.NewServer(grpc.Creds(creds)) services.RegisterProdServiceServer(rpcServer, new (services.ProdService)) listen, _ := net.Listen("tcp" , ":8081" ) rpcServer.Serve(listen) }
客户端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func main () { cert, _ := tls.LoadX509KeyPair("cert/client.pem" , "cert/client.key" ) certPool := x509.NewCertPool() ca, _ := ioutil.ReadFile("cert/ca.pem" ) certPool.AppendCertsFromPEM(ca) creds := credentials.NewTLS(&tls.Config{ Certificates: []tls.Certificate{cert}, ServerName: "localhost" , RootCAs: certPool, }) conn, err := grpc.Dial(":8081" , grpc.WithTransportCredentials(creds)) if err != nil { log.Fatal(err) } defer conn.Close() prodClient := services.NewProdServiceClient(conn) prodRes, err := prodClient.GetProdStock(context.Background(), &services.ProdRequest{ProdId: 12 }) if err != nil { log.Fatal(err) } log.Info(prodRes.ProdStock) }
https 服务端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func handler (w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hi, This is an example of https service in golang!" ) } func main () { pool := x509.NewCertPool() crt, err := ioutil.ReadFile("../cert/ca.pem" ) if err != nil { log.Fatalln("读取证书失败!" , err.Error()) } pool.AppendCertsFromPEM(crt) http.HandleFunc("/" , handler) s := &http.Server{ Addr: ":8080" , TLSConfig: &tls.Config{ ClientCAs: pool, ClientAuth: tls.RequireAndVerifyClientCert, }, } log.Fatal(s.ListenAndServeTLS("../cert/server.pem" , "../cert/server.key" )) }
客户端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func main () { pool := x509.NewCertPool() caCrt, err := ioutil.ReadFile("../cert/ca.pem" ) if err != nil { log.Fatal("read ca.crt file error:" , err.Error()) } pool.AppendCertsFromPEM(caCrt) cliCrt, err := tls.LoadX509KeyPair("../cert/client.pem" , "../cert/client.key" ) if err != nil { log.Fatalln("LoadX509KeyPair error:" , err.Error()) } tr := &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: pool, Certificates: []tls.Certificate{cliCrt}, }, } client := &http.Client{Transport: tr} resp, err := client.Get("https://localhost:8080/" ) if err != nil { panic (err.Error()) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) fmt.Println(string (body)) }