假设你通过openssl生成了如下文件:

双向认证
在开始之前,我们先讲一下什么是证书双向认证,来看一张图:

所谓证书双向认证是指:
- 服务端使用
ca.crt校验客户端的client.crt和client.key - 客户端使用
ca.crt校验服务端的server.crt和server.key
下面我们来看看用go如何实现
服务端
下面让我们来使用上面的:ca.crt, server.key, server.crt来实现一个https服务端(golang):
package mainimport ("net/http""crypto/x509""os""log""crypto/tls"
)var ca []byte = []byte(`-----BEGIN CERTIFICATE-----
MIIC+TCCAeGgAwIBAgIJALVczUCmVfmXMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCHdlLWFzLWNhMB4XDTE5MDYwMTE1NDIyMVoXDTQ2MTAxNzE1NDIyMVowEzER
MA8GA1UEAwwId2UtYXMtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDeBx93iDysi3IlXylx2rAWEwJ3P/KeY2aJJZJDdWDdMN2UARjmKcAuSKWqZsfX
Nsf/4a1AlN5U7u+DqlClPApGiVAV0mKzyw4eCy6tomvkhNE0T5s0KUcPUn6Jpei0
1Dt9dctuDBIeTGF3+DtsGACnaGjdOuwERzPCFdX96m3UYVK9iFCA1/8giSLH0TQZ
53J+CGvAt6bGir/4b64/gNHTilkh7lFgXhcN3bMoAHDVIPahnF4VIM/3fdeT12lu
PWlnKyeXLfnAC6BNQbGqze1VPjDteBj8cfTsKpZWreUss8D43eISw0Ay3WDfR0r/
5sV0PZglnDCs+Y78d4d+yatZAgMBAAGjUDBOMB0GA1UdDgQWBBQaVi/lbnGhAivI
56u3OZSf/DCfyDAfBgNVHSMEGDAWgBQaVi/lbnGhAivI56u3OZSf/DCfyDAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAMRT+HUgcVDycKBQs2dqRSaOgG
nEMuSYebMO8s6vhfYU2OVxH0SoSGsZiMvPAffy75B3keVkt0B/TO1VNiq/GYq1Xj
ne+CKwGZ5vkXflq0yTt5Njnx8nmQ/YVMnThNe3hTqJy4n/5LVaFZ/a9bVtzAt8Io
6ePGU15fj7uF6AwfoCUPKRKx7EL4WEl9mgqxMeEOLFT4+JMyMTX8iH+eEKqyUmzE
rKpcHlRxu4ZrvTt4DjzBdxeqZ0u5zL5kOlzx7ELN+oDSCDlpWXtrsbUahqYJPdvh
LV4H+cDECUfMwPfr+piXzApEYo2CN0il9Wrd7Nx9uhXBGs5S5bjjYJEMJ8/I
-----END CERTIFICATE-----
`)var serverCrt []byte = []byte(`-----BEGIN CERTIFICATE-----
MIICpTCCAY0CCQDcPcdqFvjcdjANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAh3
ZS1hcy1jYTAgFw0xOTA2MDExNTQzMjJaGA8yMTE5MDUwODE1NDMyMlowFDESMBAG
A1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
vpJtrW9dp0Rfapz4rCU3t+nOPaoryVDQPuFbxVNuh0qNX35/W0eSwVyhCXampoNK
Sn/SFt62otMa3dOd7Ea2ulQ5S/iDQCEH5ZPwTJ1PHS6gBKjDIWLOF2VkCETgFCPs
Xs+eyAxLs9ug1w63BvPuv7A3hupPXj/URj15gc2+gx0h7DGJ+ZSmfKNrPfel5jUO
v+zndS5c1A2M7RI0PwoKDJzfmSwn8ReY/emdshr2yDaR/X0boI5siEhXgqG4KmlQ
5M/1VkkS6GDJ2PSZHx+qoM7e3ZM5FGsCkyCpIXozJg34D7CgdrEpSOHlFD83N6W8
r2E61ckZkvVI+lqa2dgDKwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBWzDZjTORt
Zn9MS3rAb/PEoeNfquMMMvFD3jKowwx5BDqo7mnGxWMp5+dF2Abe/BQkl9ciRisE
kRaKb/RNiZ5gZ7yKT7z+5tmOh1igpUtstnNDcuKyPoCAECnD8ioKkfRt9oBiHoW8
GwieNpQ3+nN6EXWGgkryYPhJSl98EAc27SaSLMzEMMIkeH3Za1w3gpOveSHiM4cA
B2NGYB6CkETXKqXTH7qKiA8/Z22mngTCoQBOmys5Im5RXEwlRyVSGhLkbVMAzLhg
GbBibjQ2dauMTbe1yunJE4fQPdGq9UFkljTG0aek820yYAeMc42oiXVNqlV+sOG5
JjUjkCMNvVG6
-----END CERTIFICATE-----
`)var serverKey []byte = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAvpJtrW9dp0Rfapz4rCU3t+nOPaoryVDQPuFbxVNuh0qNX35/
W0eSwVyhCXampoNKSn/SFt62otMa3dOd7Ea2ulQ5S/iDQCEH5ZPwTJ1PHS6gBKjD
IWLOF2VkCETgFCPsXs+eyAxLs9ug1w63BvPuv7A3hupPXj/URj15gc2+gx0h7DGJ
+ZSmfKNrPfel5jUOv+zndS5c1A2M7RI0PwoKDJzfmSwn8ReY/emdshr2yDaR/X0b
oI5siEhXgqG4KmlQ5M/1VkkS6GDJ2PSZHx+qoM7e3ZM5FGsCkyCpIXozJg34D7Cg
drEpSOHlFD83N6W8r2E61ckZkvVI+lqa2dgDKwIDAQABAoIBAQC6IDmOkp5dp6Gp
dvZI63Cn52rPP0zUqmE5iNEgwIPLDz1Uby/j7tuejuGZZJEPQFtmt8BxJcQq8xPi
Y7Rx9/6vrWLomCdYkuorh3nC2kFStx8CbfFmwiGXKCezC9Hu2ccaMp1ZtOibGX7V
jEGmQMiF343b7yzlWGHy2Ee8Rz4yvqlPpje9vINU01DYbJKV5jpLtWYuTulKi0zf
78N9cMBnif8AO4qNZBMjo0dpfEripOrbvNkuxTm65x4sFeF0yPms8GDYD+beooEF
EdxPHdIwRnDx0ck/amkmhZVC2yH45M09Z6iib5Zl+B6UVNOSTFM5LvzNlvf22o3e
Xnsiy0KBAoGBAOgV1oaHRqKfpd+h1Nul6OfUIQogk55OqCkR7wLt5mxaa0xCUn2w
OMiJeR2WEWZHufpVajsjoBrSbqvLbRLqUscrTs0sgik8IpT12QKen9kJJcdddzKD
aNfaD+WVwA20VuwPbd+L6nllOH7DZtgysCQEfFlP37JCRmMEjfpkdNdpAoGBANI1
g3V25Sjw9hl/SSxXTKGWBCuDjeIopz1618veap+tvJJvOJYHOBS8RtyG46G4Ugw3
IQgNdpaQsxRMOhAK1kOhNhpnNNLFAJ2cm+sYcJVaAG+dynSgvGGf02FhXOP/C9f/
qkzsV9FNitt3pIm8zw0UN0+J/fyeAei1RypbMGdzAoGBALNRDCttIbpMt5COLTR4
f/d/AvgcK3JJO8xfutf8j+hwBC8rnyjVm0n2Tcn6RP9Ns/gjPqzq3a1boX7C8keH
HOYeJAiKtxa9C8skGMPZY5ABbVsYcBxrQ/pi1Z2Bkp4EFJTXZwEtzcB14KywtSme
IFHz1U/8Us4cPt4KithH/a7hAoGBAL8CT1zKV7sXEZjjl3sKLKDbrxhXJvLtW+I6
oKIojZxhA2vQUovJLYVx+7XhgDBwS2W8JnCpwytXetIj3dK79ixn7cCaLV6kEkYl
i2xZvduId8L0j4XglKzkzO+8x+qI05tHPtk9HSMcIeQA2GssPLw2tXe5/Sex8Cwj
pPHxAI/PAoGAMk2NcYGKHl16Fswf4Q4sEQxD5S5TxNQfNpF9EK12WXUEldS/cnpT
IU5OHpQJ5kXrDAoVeU3I2Qogy58F0WF1FznKRLsOIsvEczqrUXx+foPNmhLqFW0l
oPmNo40QWbQCjtZGE4fpZZPcsbVTTF2twlahPaDxZYNV85oNXy5C+Ec=
-----END RSA PRIVATE KEY-----
`)func main() {cert, err := tls.X509KeyPair(serverCrt, serverKey)if err != nil {log.Println(err)os.Exit(1)}pool := x509.NewCertPool()pool.AppendCertsFromPEM(ca)s := http.Server{Addr: ":8443",Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {log.Println(r.Method, r.URL.String())w.Write([]byte("hello world"))}),TLSConfig: &tls.Config{ClientCAs: pool,Certificates: []tls.Certificate{cert},ClientAuth: tls.RequireAndVerifyClientCert,},}err = s.ListenAndServeTLS("", "")if err != nil {log.Println(err)}
}
当你使用curl https://localhost:8443时会得到如下错误:
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.htmlcurl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
再试试curl https://localhost:8443 --cacert ca.crt:
curl: (35) error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
证书校验失败
再试试curl https://localhost:8443 --cacert ca.crt --cert client.crt --key client.key,我们正确的拿到了服务端的返回结果,这就是双向认证图中的绿色线(服务端校验客户端)
客户端
使用ca.crt, client.crt, client.key来实现客户端,如下:
package mainimport ("io/ioutil""time""net/http""crypto/x509""os""log""crypto/tls"
)var ca []byte = []byte(`-----BEGIN CERTIFICATE-----
MIIC+TCCAeGgAwIBAgIJALVczUCmVfmXMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCHdlLWFzLWNhMB4XDTE5MDYwMTE1NDIyMVoXDTQ2MTAxNzE1NDIyMVowEzER
MA8GA1UEAwwId2UtYXMtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDeBx93iDysi3IlXylx2rAWEwJ3P/KeY2aJJZJDdWDdMN2UARjmKcAuSKWqZsfX
Nsf/4a1AlN5U7u+DqlClPApGiVAV0mKzyw4eCy6tomvkhNE0T5s0KUcPUn6Jpei0
1Dt9dctuDBIeTGF3+DtsGACnaGjdOuwERzPCFdX96m3UYVK9iFCA1/8giSLH0TQZ
53J+CGvAt6bGir/4b64/gNHTilkh7lFgXhcN3bMoAHDVIPahnF4VIM/3fdeT12lu
PWlnKyeXLfnAC6BNQbGqze1VPjDteBj8cfTsKpZWreUss8D43eISw0Ay3WDfR0r/
5sV0PZglnDCs+Y78d4d+yatZAgMBAAGjUDBOMB0GA1UdDgQWBBQaVi/lbnGhAivI
56u3OZSf/DCfyDAfBgNVHSMEGDAWgBQaVi/lbnGhAivI56u3OZSf/DCfyDAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAMRT+HUgcVDycKBQs2dqRSaOgG
nEMuSYebMO8s6vhfYU2OVxH0SoSGsZiMvPAffy75B3keVkt0B/TO1VNiq/GYq1Xj
ne+CKwGZ5vkXflq0yTt5Njnx8nmQ/YVMnThNe3hTqJy4n/5LVaFZ/a9bVtzAt8Io
6ePGU15fj7uF6AwfoCUPKRKx7EL4WEl9mgqxMeEOLFT4+JMyMTX8iH+eEKqyUmzE
rKpcHlRxu4ZrvTt4DjzBdxeqZ0u5zL5kOlzx7ELN+oDSCDlpWXtrsbUahqYJPdvh
LV4H+cDECUfMwPfr+piXzApEYo2CN0il9Wrd7Nx9uhXBGs5S5bjjYJEMJ8/I
-----END CERTIFICATE-----
`)var clientCert []byte = []byte(`-----BEGIN CERTIFICATE-----
MIICpTCCAY0CCQDcPcdqFvjcdzANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAh3
ZS1hcy1jYTAgFw0xOTA2MDExNTQ0MTdaGA8yMTE5MDUwODE1NDQxN1owFDESMBAG
A1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
tUTWs7oc12yaZy8v6xcB3om76IqQMgmvDEzb9FaQ75bDCfMWB3DcwALTIkX6aish
z7L0dgoNPBN7z3WDXAMwyTRNJzUKP01Eygc9sUbNtk9fDa6gBlJwZpm6ofrDgoRB
FY02zOG0Fztl9GVVUZ/ZyRyzaUvPMk2sDy/a9CO/5/xwim/rWT3opSEVShYPuO4i
ZPjVbY0AyfTrxdu9RhdhGYPGatTm2SFvSVgs23bgSG4qO6MOvUhvUJeD2GJVnqVc
oCGATxCXrOnt60K22LgxehTdTSORg4J7N/f6jS4KEcybaQpitJXrPKozd+x3zEBu
z34hVEN3/jaO0C9MUVu4awIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCnAJ+va7e0
GfObQQKQ5iAKtLIno6KbgxuXifbDvyLofammSe2LFihr0ql5KByX7rNdczvQde9d
EEbJnAbKG+2kE8m5iYR26M0+I0AbRANNEAyZHP6Ig0K9XXofDv2/RKFeK5frzEz1
fexqFmHV6lTevvcaUpoIpCZyjXQf4Ni6DmmYb88S39HLRcvNEVEu94ums0IWb3kr
SrxCrKVhBzgk25Lt3668zdeTnMfYuAGXDR2s4z6/unfYcxnI9Iv8Cn83LuAn7XeH
GuRCV1hQF2NLydfh/KleayVSsjJD1flqjynlmisXqu+HtUfBhUKRq9YKu8Uvktjn
Zl8jsC2ARN6I
-----END CERTIFICATE-----
`)var clientKey []byte = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAtUTWs7oc12yaZy8v6xcB3om76IqQMgmvDEzb9FaQ75bDCfMW
B3DcwALTIkX6aishz7L0dgoNPBN7z3WDXAMwyTRNJzUKP01Eygc9sUbNtk9fDa6g
BlJwZpm6ofrDgoRBFY02zOG0Fztl9GVVUZ/ZyRyzaUvPMk2sDy/a9CO/5/xwim/r
WT3opSEVShYPuO4iZPjVbY0AyfTrxdu9RhdhGYPGatTm2SFvSVgs23bgSG4qO6MO
vUhvUJeD2GJVnqVcoCGATxCXrOnt60K22LgxehTdTSORg4J7N/f6jS4KEcybaQpi
tJXrPKozd+x3zEBuz34hVEN3/jaO0C9MUVu4awIDAQABAoIBAAvmHunhV69Uc7Y+
RLj746WGCQ20us5uEE2QZgfd/tmbXeYzTMjkQblg9hcT3OJHPorxxlZRRpkg9kmh
/yN1Gii7BC2Er82D9vYED6qpaSuEfkrZoauIkdRKVxP28AqLP/J4OJauYjH8Ni8a
z8Tx50vqVGSfE1TMAHVmwMqx5hEGRtyG6wgy3sYvox0P661L/NJhWpV/kmlgNO/n
K06HAPHP/iHvVXIdYHAJJIVYimjBIax/sZL5+haWddYshNkBJ5Hhaw1XjU/HQWvX
Q18T0RKBli1aeoOOGLQnasKC6NzpBk4Q158+CaluUVf/RzY44fohUEeMqgubHuJp
YprMn8ECgYEA2YwSJnI2iNOrxh1zh03nMSUYErXFErIx2qHKBZ76S8VIStMAIwVe
glQAdLxLbj+3zuj2YhUnT+sZplgurN3jwcjb97XTbLn/aw0TxnbJmv3KPH6Wq7Vd
DRKVQSwR/CRsXR605vPcpNRSEKRfQ1SNhdgxitgzUUOQeSxT5xVBHdECgYEA1U8w
sF2Srqlx5kfyWPpXJqpEMwRMR4kG4vTu5KiF92hoticJhnLVsnAwxLVNf4ZMw/Vn
pEkuvvYPwGxsmzazDQh8Gw9iYWDaBA1kL3Cee3M1C76utKZ6ZCJIuYCrByd2ME0A
w4NzmQkD0+atB2QOTR2p/WF3etsweBfC0WhsVXsCgYAVrDeurtgx/2xwe0SkKSWs
JrbkPkmY2DnRPycCMllbLRdLpQOxeXp132qANrYJEL3+FgVdth/JfXF7ufNEc4Ka
LqmDXxDmFw2UG6RptDHXiAsaxb2684GGqOBHst1D0lkdWc7J52eG4EQgtk9rRMQo
nmYpH+rU4LdG6xycu+hV0QKBgG1hbDAj64GQ9g0Fu6oQxPvYt5wJiivsghGDU7UB
DaEucvNk1SeSXy5fBUL5TUIlVdvuTTUbKdNWTgF4F8EHrYzzWuBtZR9WELWfQE5r
S3k2PG9HWkLcU0phojUtW4YRoDNoaQnYsEA7NTFFylhN4F9+5Jo/jor7NsF+PbIv
/81dAoGAb/SIjrn3bWoRQD+Z4JmOJm2dnGGowqiZ4i6PSxBO4clveCNhXdUFF8Va
ZzXzWh1ER0RMGc0UFsPT4lhckliy8R6lX2cj1nE7sDbIjia9B2thpCmX6wrvG/Z/
zEAzM7mWucs6NFvkLsXsEtpL/V5AUvd/J9nQrPDOWdvbp18jUR4=
-----END RSA PRIVATE KEY-----
`)func main() {cert, err := tls.X509KeyPair(clientCert, clientKey)if err != nil {log.Println(err)os.Exit(1)}pool := x509.NewCertPool()pool.AppendCertsFromPEM(ca)client := http.Client{Timeout: 3 * time.Second,Transport: &http.Transport{TLSClientConfig: &tls.Config{Certificates: []tls.Certificate{cert},RootCAs: pool,},},}req, err := http.NewRequest(http.MethodGet, "https://localhost:8443", nil)if err != nil {log.Println(err)os.Exit(1)}resp, err := client.Do(req)if err != nil {log.Println(err)os.Exit(1)}defer resp.Body.Close()data, _ := ioutil.ReadAll(resp.Body)log.Println(string(data))
}
总结



















