证书双向认证

假设你通过openssl生成了如下文件:
证书双向认证_第1张图片

双向认证

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

所谓证书双向认证是指:

  • 服务端使用ca.crt校验客户端的client.crtclient.key
  • 客户端使用ca.crt校验服务端的server.crtserver.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))
}

总结

证书双向认证_第3张图片

你可能感兴趣的:(rest)