双向认证
C/S两端分别生成密钥对,交换公钥,基于公钥验证另一端。
第一步,创建密钥对,把公钥存储为自签名、PEM编码的证书。用openssl即可,这里以一个HTTPS服务端为例:
openssl req -nodes -x509 -newkey rsa:4096 -keyout serverKey.pem -out serverCrt.pem -days 365
serverKey.pem包含服务器的私钥,需要保护;serverCrt.pem包含服务器的公钥,用来分发。
客户端:
openssl req -nodes -x509 -newkey rsa:4096 -keyout clientKey.pem -out clientCrt.pem -days 365
服务端
https服务端源码:
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Hello: %s\n", r.TLS.PeerCertificates[0].Subject.CommonName)
fmt.Fprint(w, "Authentication successful")
}
func main() {
var (
err error
clientCert []byte
pool *x509.CertPool
tlsConf *tls.Config
server *http.Server
)
http.HandleFunc("/hello", helloHandler)
if clientCert, err = ioutil.ReadFile("../client/clientCrt.pem"); err != nil {
log.Fatalln(err)
}
pool = x509.NewCertPool()
pool.AppendCertsFromPEM(clientCert)
tlsConf = &tls.Config{
ClientCAs: pool,
ClientAuth: tls.RequireAndVerifyClientCert,
}
tlsConf.BuildNameToCertificate()
server = &http.Server{
Addr: ":9443",
TLSConfig: tlsConf,
}
log.Fatalln(server.ListenAndServeTLS("serverCrt.pem", "serverKey.pem"))
}
访问https://localhost:9443/hello,页面会显示Your connection isn’t private。服务端也显示:http: TLS handshake error from [::1]:1157: remote error: tls: unknown certificate
用curl测试:
$ sudo curl -ik -X GET --cert clientCrtBad.pem --key clientKeyBad.pem https://localhost:9443/hello
curl: (16) SSL_write() returned SYSCALL, errno = 32
服务端:
http: TLS handshake error from 127.0.0.1:33470: tls: failed to verify client certificate: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "Internet Widgits Pty Ltd")
再测试正确的密钥,请求连接成功:
$ sudo curl -ik -X GET --cert clientCrt.pem --key clientKey.pem https://localhost:9443/hello
HTTP/2 200
content-type: text/plain; charset=utf-8
content-length: 25
date: Mon, 28 Feb 2022 14:00:00 GMT
Authentication successful
服务端也显示了 Hello:,完成了对客户端的验证。
客户端(问题残留)
func main() {
var (
err error
cert tls.Certificate
serverCert, body []byte
pool *x509.CertPool
tlsConf *tls.Config
transport *http.Transport
client *http.Client
resp *http.Response
)
if cert, err = tls.LoadX509KeyPair("clientCrt.pem", "clientKey.pem"); err != nil {
log.Fatalln(err)
}
if serverCert, err = ioutil.ReadFile("../server/serverCrt.pem"); err != nil {
log.Fatalln(err)
}
pool = x509.NewCertPool()
pool.AppendCertsFromPEM(serverCert)
tlsConf = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
}
tlsConf.BuildNameToCertificate()
transport = &http.Transport{
TLSClientConfig: tlsConf,
}
client = &http.Client{
Transport: transport,
}
if resp, err = client.Get("https://localhost:9443/hello"); err != nil {
log.Fatalln(err)
}
if body, err = ioutil.ReadAll(resp.Body); err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
fmt.Printf("Success: %s\n", body)
}
不过在测试的时候因为域名问题报错了,暂未解决:
Get "https://localhost:9443/hello": x509: certificate is not valid for any names, but wanted to match localhost
|