TLS配置
非对称加密流程
TLS流程
这是单向TLS流程
ECDHE握手过程(图片来自透视Http协议课程)
浏览器发送Client Hello消息
- 客户端向服务器打招呼,消息中包含客户端生成的随机数C,客户单的TLS版本号,可使用的密码套件列表及扩展列表。
- 后续需要对比TLS版本号,用随机数计算秘钥。
服务器发送Server Hello消息
- 服务器向客户端打招呼,消息包含服务器生成的随机数S,确认TLS版本号,从客户端可用密码套件列表中选用的密码套件。
- 还需包含数字证书,用于验证。
- 以及秘钥交换算法的参数(也就是公钥),需包含签名认证。
- 并确认已收到了Client Hello信息。
客户端验证并计算主密钥
- 对收到的证书和签名进行验证。根证书对服务器证书进行验签,根证书一般都是内置和预安装的
- 验证成功后向服务器发送送秘钥交换算法的参数。
- 服务器接收到客户端的秘钥交换算法参数(也就是私钥),开始计算master secret。先生成pre-master,根据pre-master和两个随机数算出master secret(主秘钥)。
- 客户端也进行主秘钥的计算。之所以称为主密钥,是因为根据主密钥可生成多个会话秘钥由于不同的具体加密,如:客户端发送用的会话秘钥、服务端用的会话秘钥。
- 目前都是明文通信,所以秘钥的生成必须很讲究,各部计算都需随机性极强的算法,保证安全性。
验证加密通信并验证
至此加密通信所用的秘钥都已生成,在此之前都是明文发送。客户端通知服务器启用加密通信并加密发送之前所发信息的摘要,服务器也做一样的事,用于验证加密通信是否可行。发送的两个消息分别是“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
服务器证书:
- 服务器根证书和私钥
- 生成服务器私钥
- 用服务器私钥生成证书请求
- 服务器根证书和根私钥+证书请求生成服务器证书
go grpc 配置
单向TLS
服务端
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
}
双向TLS
可以理解成多一个反过来的流程,双方对等
服务端
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))
}