GO自签名证书双向认证

· by author · Read in about 2 min · (255 words)

生成SSL证书

先按照以上操作生成自签名证书。服务端和客户端分别需要引用到的证书如下:

服务端证书 客户端证书
ca.crt ca.crt
server.crt client.crt
server.key client.key

服务端

在go中启动一个https 服务,可以调用http.server 对象的 ListenAndServeTLS(certFile, keyFile string)方法。 通过注释和实现代码可以知道,该方法的两个参数分别是证书和私钥。也就是server.crtserver.key。并且同时还从http.server对象中获取了一个TLSConfig。TLSConfig 中设置的是根证书ca.crt

http.server的数据结构: http.server

因此启动一个https server,无其它需求时,简单设置其三个参数即可:
1. Addr
2. Handler
3. TLSConfig

  • Addr 是一个设置服务TCP地址(包括端口号)的字符串。

  • 准备TLSConfig 参数,读取根证书文件。

   pool := x509.NewCertPool()
	caCertPath := "ca.crt"

	caCrt, err := ioutil.ReadFile(caCertPath)
	if err != nil {
		fmt.Println("ReadFile err:", err)
		return
	}
	pool.AppendCertsFromPEM(caCrt)
  • 准备Handler参数,实现一个带有 http.ResponseWriter和http.Request 的函数,作为处理客户端http请求的响应。
type myhandler struct {
}

func (h *myhandler) ServeHTTP(w http.ResponseWriter,
	r *http.Request) {
	fmt.Fprintf(w,
		"Hi, This is an example of http service in golang!\n")
}
  • 完整代码
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,
		"Hi, This is an example of http service in golang!\n")
}

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)
     //初始化一个server 实例。
	s := &http.Server{
		//设置宿主机的ip地址,并且端口号为8081
		Addr:    ":8081",
		Handler: &myhandler{},
		TLSConfig: &tls.Config{
			ClientCAs:  pool,
			ClientAuth: tls.RequireAndVerifyClientCert,

		},
	}

	err = s.ListenAndServeTLS("server.crt", "server.key")

	if err != nil {
		fmt.Println("ListenAndServeTLS err:", err)
	}
}

客户端

客户端访问服务端的时候,设置其Transport参数。在构建Transport参数的时候,设置根证书ca.crtclient.crtclient.key

  • 完整代码
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)

	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}
	//这里的ip地址需要在生成自签名证书的时候指定,否则ssl验证不通过。
	resp, err := client.Get("https://127.0.0.1:8081") 
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	fmt.Println(string(body))
}

Go中的https 双向证书验证便是以上实现。客户端证书只要设置不正确,访问服务端的资源就会失败。https的双向证书验证,是保证数据在客户端和服务端传输时的一种最安全的做法。