Go代码打通HTTPs

构建 CA 证书链

   我们首先要创建 client/server 使用的证书。创建证书的方法有很多种:不怕麻烦,直接通过openssl创建的,有通过cfssl创建的。这里要介绍的是比较简单的一种:tls-gen。

   tls-gen是一个用Python编写的、非常易用的工具。它定义了三种profile。这里我们选择最简单的一种:一个根证书和一组证书、私钥对。

   在shell里面执行一下命令:

git clone https://github.com/michaelklishin/tls-gen
cd tls-gen/basic
make CN=www.mytestdomain.io

   就这样,我们就为域名 www.mytestdomain.io 创建了一套证书。观察一下当前路径的内容,我们会发现两个新的目录:testca 和 server。前者里面存放了刚刚创建的根证书 (root CA),后者里面存放了我们之后的服务程序要用的的证书和私钥。

testca/
  cacert.pemserver/
  cert.pem
  key.pem
编写服务

 

   接下来开始写代码。Go 对 TLS 的支持还是比较完备的,也比较简单。以下是服务端的代码(server.go):

func HelloServer(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Write([]byte("This is an example server.\n"))
}
func main() {
    http.HandleFunc("/hello", HelloServer)
    err := http.ListenAndServeTLS(":1443", "server/cert.pem", "server/key.pem", nil)    
   if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

   可以看到我们创建了一个HTTP服务,这个服务监听1443端口并且只处理一个路径/hello.然后调用了下面这个函数来监听1443端口。注意我们给出了之前创建的服务的证书和私钥,这样就保证了HTTP会用加密的方式来传输。

ListenAndServeTLS(addr, certFile, keyFile string, handler Handler)

   运行服务程序:

go run server.go
访问HTTPs服务

   假定我们的服务程序是运行在本地的。我们先改一下/etc/hosts来配置域名解析:

# echo 127.0.0.1 www.mytestdomain.io >> /etc/hosts

   我们用以下的代码(client.go)来访问服务:

func main() {    client := &http.Client{}
    resp, err := client.Get("https://www.mytestdomain.io:1443/hello")    
    if err != nil {
        panic("failed to connect: " + err.Error())
    }
    content, _ := ioutil.ReadAll(resp.Body)
    s := strings.TrimSpace(string(content))

    fmt.Println(s)
}

​​​​​​​    运行 go run client.go,只能得到这样的错误:

panic: failed to connect: Get https://www.mytestdomain.io:1443/hello: x509: certificate signed by unknown authorit

​​​​​​​    这是因为系统不知道如何来处理这个 self signed 证书。

    各个 OS 添加根证书的方法是不同的。对于 Linux 系统 (以 Ubuntu 为例) 来说,把证书文件放到相应的目录即可:

# sudo cp testca/cacert.pem /etc/ssl/certs

​​​​​​​    如果是 macOS,可以用一下的命令:

# sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain  testca/cacert.pem

​​​​​​​   上面的方法会把我们手工创建的 root CA添加到系统所已知的列表里面。这样一来,所有用该 root CA 创建的证书都可以被认证了。

 

   假如只是一个普通的用户,没有root/sudo权限,不就无法做上面的操作了吗?这种情况下还有另外一个做法:把root CA 放置在代码里面。

   在上面的client.go 里面添加这么几行代码:

func main() {
    roots := x509.NewCertPool()
    ok := roots.AppendCertsFromPEM([]byte(rootPEM))    
    if !ok {        panic("failed to parse root certificate")
    }

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{RootCAs: roots},
    }

    client := &http.Client{Transport: tr}    // ...

   其中的rootPEM 就是 testca/cacert.pem 的内容

var rootPEM = `-----BEGIN CERTIFICATE-----MIIDAjCCAeqgAwIBA
gIJAL2faqa73yLvMA0GCSqGSIb3DQEBCwUAMDExIDAeBgNVBAMMF1RMU0dl
blNlbGZTaWduZWR0Um9vdENBMQ0wCwYDVQQHDAQkJCQkMB4XDTE4MDIwNTA
5Mzc0NVoXDTI4MDIwMzA5Mzc0NVowMTEgMB4GA1UEAwwXVExTR2VuU2VsZl
NpZ25lZHRSb290Q0ExDTALBgNVBAcMBCQkJCQwggEiMA0GCSqGSIb3DQEBA
QUAA4IBDwAwggEKAoIBAQC9eO6Tam4XFDUbK9FAStAg29teYeKtt8WEJvK
GB50xMfXO2pD0StsXhKrspXBYck0FwKIBsTLr97w7dSqa64z3U2V2Borog
FzoEE4JH2sydYGAQqNAqezGx8VZnQVRyZEBifRPebR4WVD5GtXYe+MnSkH
PIgsG0QG0SaiSfMl05dSJHoE9T9Kly9fH6yED88++OYjZZRGKOf2THpQlX
JjF3iwCDLkwz9Z/kjmpK/rR0SEhtanf7bOgGs3OoFmX4DvmFJXoriVUC9jc
j0Z4oX3Ld81XXyd4FJkpKvdKDhYkqcugFgERqdBeRDM+MA38YooKHZh0klL
2EThNXJxM0r1vAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgE
GMA0GCSqGSIb3DQEBCwUAA4IBAQBEqp0ON1A/pCKFztfKuzdW+9pauE8dl6Ij
3++dt6AqW5QYFLOEFQwoMBOkGChGQDxHkakyaA0DfGe5JntMH0yYyZnr4kfs+
AcY6P+2PfgrgVBqadhR6uAGOBaXDW7dlllqIJJ8NRInA/fTDYXMxBJbFrcj2c
GIYVPvAbrosZ5L/YdAdVM76V8uuk8Hmmy5zRQj+gWt/jDkYWFrp0b6k3FBXvM7
+nhqAIdyMjLioAdYwFpPglGj3xHXS5neWjyUDlAYISNe+PKMERSeDrptyDE+ljz
l77hvvfZD9OPhXbDkAeVU/NaDwHG/G5HDVdNbg/FZ6ueevF34Xuzejm3lrdJm-----E
ND CERTIFICATE-----`

也就是说,我们用准备好的 root CA 的内容产生了一个新的 http transport。

运行一下 go run client.go。成功!

   This is an example server.

 

总结

  一对 HTTPs client/server 程序中需要一个共同的 root CA。服务器端需要该 root CA,创建的 CA/私钥对。

 

参考:

https://www.yuque.com/docs/share/cab8aeae-8cb8-4aeb-9977-fd3d1c8f8af9

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值