所谓证书双向认证是指:
ca.crt
校验客户端的client.crt
和client.key
ca.crt
校验服务端的server.crt
和server.key
下面我们来看看用go如何实现
下面让我们来使用上面的:ca.crt
, server.key
, server.crt
来实现一个https服务端(golang):
package main
import (
"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.html
curl 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 main
import (
"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))
}