双向认证
先清理下环境
rm -rf ca.crt ca.key server.key server.csr server.crt
不仅仅要认证服务端,也要认证客户端。
自签CA证书
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=ca-01" -days 5000 -out ca.crt
服务端证书
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=server-02" -out server.csr
openssl x509 -req -in server.csr -extfile san.cnf -extensions v3_req -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
openssl req -in server.csr -text -noout
openssl x509 -in server.crt -text -noout
客户端证书
服务端可以要求对客户端的证书进行校验,以更严格识别客户端的身份,限制客户端的访问。要对客户端数字证书进行校验,首先客户端需要先有自己的证书。
golang的tls要校验ExtKeyUsage,可以在生成时指定extKeyUsage, 创建client.ext,内容: extendedKeyUsage=clientAuth
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=client-02 " -out client.csr
cat > client.ext << EOF
extendedKeyUsage=clientAuth
EOF
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile client.ext -out client.crt -days 5000
openssl x509 -in client.crt -text -noout
CA:
私钥文件 ca.key
数字证书ca.crt
Server:
私钥文件 server.key
数字证书 server.crt
Client:
私钥文件 client.key
数字证书 client.crt
# ls
ca.crt ca.key server.crt server.csr server.key
client.key client.csr client.crt
服务端
//server-v2.go
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)
type myhandler struct {
}
func (h *myhandler) ServeHTTP(w http.ResponseWriter,r *http.Request) {
fmt.Fprintf(w, "这是HTTPS页面,双向认证完成,我们可以通信了!\n")
}
func main() {
pool := x509.NewCertPool()
caCertPath := "ca.crt"
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
//加载用于校验数字证书的ca.crt
pool.AppendCertsFromPEM(caCrt)
s := &http.Server{
Addr: ":8081",
Handler: &myhandler{},
TLSConfig: &tls.Config{
ClientCAs: pool,//用于校验客户端证书
ClientAuth: tls.RequireAndVerifyClientCert,
// 通过将 tls.Config.ClientAuth 赋值为 tls.RequireAndVerifyClientCert 来实现 Server 强制校验 client 端证书
},
}
err = s.ListenAndServeTLS("server.crt", "server.key")
if err != nil {
fmt.Println("ListenAndServeTLS err:", err)
}
}
客户端
//client-v2.go
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
pool := x509.NewCertPool()
caCertPath := "ca.crt"
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
pool.AppendCertsFromPEM(caCrt)
// 需要加载 client.key 和 client.crt 用于 server 端连接时的证书校验
cliCrt, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
fmt.Println("Loadx509keypair err:", err)
return
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{cliCrt},
},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://server-01:8081")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
go build server-v2.go
go build client-v2.go
验证过程
验证1: 通过域名
修改 client-v2.go 中代码
resp, err := client.Get("https://server-01:8081")
验证2: 通过IP
修改 client-v2.go 中代码
resp, err := client.Get("https://127.0.0.1:8081")