非对称加密流程
这是单向TLS流程
ECDHE握手过程(图片来自透视Http协议课程)
至此加密通信所用的秘钥都已生成,在此之前都是明文发送。客户端通知服务器启用加密通信并加密发送之前所发信息的摘要,服务器也做一样的事,用于验证加密通信是否可行。发送的两个消息分别是“Change Cipher Spec”和“Finished”。之后就可使用加密的Http请求和响应。
上述方式验证了服务器的安全性,而服务器验证客户端可用更多方式,比如账号密码,此时已经达到了加密通信的标准。
更深层次的客户端验证,也可Client Hello中出示客户端证书,用以验证。最终实现更为安全的双向验证。
openssl.cnf
[req]
distinguished_name = req_distinguished_name
attributes = req_attributes
[req_distinguished_name]
[req_attributes]
[test_ca]
basicConstraints = critical,CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
keyUsage = critical,keyCertSign
[test_server]
basicConstraints = critical,CA:FALSE
subjectKeyIdentifier = hash
keyUsage = critical,digitalSignature,keyEncipherment,keyAgreement
subjectAltName = @server_alt_names
[server_alt_names]
DNS.1 = *.grpc.test.com
[test_client]
basicConstraints = critical,CA:FALSE
subjectKeyIdentifier = hash
keyUsage = critical,nonRepudiation,digitalSignature,keyEncipherment
extendedKeyUsage = critical,clientAuth
#!/bin/bash
# Create the server CA certs. 服务器根证书和私钥
openssl req -x509 \
-newkey rsa:4096 \
-nodes \
-days 3650 \
-keyout ca_key.pem \
-out ca_cert.pem \
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-server_ca/ \
-config ./openssl.cnf \
-extensions test_ca \
-sha256
# Create the client CA certs. 客户端根证书和私钥
openssl req -x509 \
-newkey rsa:4096 \
-nodes \
-days 3650 \
-keyout client_ca_key.pem \
-out client_ca_cert.pem \
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client_ca/ \
-config ./openssl.cnf \
-extensions test_ca \
-sha256
# Generate a server cert.
# 长度4096
# 生成服务器私钥
openssl genrsa -out server_key.pem 4096
# 私钥生成服务器证书请求
openssl req -new \
-key server_key.pem \
-days 3650 \
-out server_csr.pem \
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-server1/ \
-config ./openssl.cnf \
-reqexts test_server
# 生成服务器证书
openssl x509 -req \
-in server_csr.pem \
-CAkey ca_key.pem \
-CA ca_cert.pem \
-days 3650 \
-set_serial 1000 \
-out server_cert.pem \
-extfile ./openssl.cnf \
-extensions test_server \
-sha256
openssl verify -verbose -CAfile ca_cert.pem server_cert.pem
# Generate a client cert. 客户端证书
# 长度4096
openssl genrsa -out client_key.pem 4096
openssl req -new \
-key client_key.pem \
-days 3650 \
-out client_csr.pem \
-subj /C=US/ST=CA/L=SVL/O=gRPC/CN=test-client1/ \
-config ./openssl.cnf \
-reqexts test_client
openssl x509 -req \
-in client_csr.pem \
-CAkey client_ca_key.pem \
-CA client_ca_cert.pem \
-days 3650 \
-set_serial 1000 \
-out client_cert.pem \
-extfile ./openssl.cnf \
-extensions test_client \
-sha256
openssl verify -verbose -CAfile client_ca_cert.pem client_cert.pem
rm *_csr.pem
服务器证书:
服务端
package server
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"log"
"os"
)
func GetTlsOpt() grpc.ServerOption {
// 服务端证书、服务端密钥
creds, err := credentials.NewServerTLSFromFile("x509/server_cert.pem", "x509/server_key.pem")
if err != nil {
log.Fatal(err)
}
return grpc.Creds(creds)
}
客户端
package client
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"log"
"os"
)
// 单向TLS
func GetTlsOpt() grpc.DialOption {
// 服务端根证书,域名,根证书用来做验签
creds, err := credentials.NewClientTLSFromFile("x509/ca_cert.pem", "echo.grpc.test.com")
if err != nil {
log.Fatal(err)
}
opt := grpc.WithTransportCredentials(creds)
return opt
}
可以理解成多一个反过来的流程,双方对等
服务端
package server
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"log"
"os"
)
func GetMTlsOpt() grpc.ServerOption {
cert, err := tls.LoadX509KeyPair("x509/server_cert.pem", "x509/server_key.pem")
if err != nil {
log.Fatal(err)
}
ca := x509.NewCertPool()
caFilePath := "x509/client_ca_cert.pem"
bytes, err := os.ReadFile(caFilePath)
if err != nil {
log.Fatal(err)
}
if ok := ca.AppendCertsFromPEM(bytes); !ok {
log.Fatal("ca append failed")
}
tlsConfig := &tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{cert},
ClientCAs: ca,
}
return grpc.Creds(credentials.NewTLS(tlsConfig))
}
客户端
package client
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"log"
"os"
)
func GetMTlsOpt() grpc.DialOption {
// 客户端证书和密钥
cert, err := tls.LoadX509KeyPair("x509/client_cert.pem", "x509/client_key.pem")
if err != nil {
log.Fatal(err)
}
ca := x509.NewCertPool()
// 服务端根证书
caFilePath := "x509/ca_cert.pem"
bytes, err := os.ReadFile(caFilePath)
if err != nil {
log.Fatal(err)
}
if ok := ca.AppendCertsFromPEM(bytes); !ok {
log.Fatal("ca append failed")
}
tlsConfig := &tls.Config{
ServerName: "echo.grpc.test.com",
Certificates: []tls.Certificate{cert},
RootCAs: ca,
}
return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
}