雙向認證
先清理下環境
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("//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("//server-01:8081")
驗證2: 通過IP
修改 client-v2.go 中代碼
resp, err := client.Get("//127.0.0.1:8081")