【实践】如何在本地环境用GO实现HTTPS链接?

1,摘要

本篇文章是基于实操,如何在本地环境用GO实现HTTPS链接。原理部分请参考文章《【深度知识】HTTPS协议原理和流程分析》

2,crt、key以及pem的区别

  • 证书(Certificate) .cer .crt

  • 私钥(Private Key) .key

  • 证书签名请求(Certificate sign request) .csr

  • x509
    X.509是一种非常通用的证书格式。所有的证书都符合ITU-T X.509国际标准,因此(理论上)为一种应用创建的证书可以用于任何其他符合X.509标准的应用。
    x509证书一般会用到三类文,key,csr,crt。 Key 是私用密钥openssl格,通常是rsa算法。 Csr 是证书请求文件,用于申请证书。在制作csr文件的时,必须使用自己的私钥来签署申,还可以设定一个密钥。 crt是CA认证后的证书文,(windows下面的,其实是crt),签署人用自己的key给你签署的凭证。

  • PEM - Privacy Enhanced Mail,打开看文本格式,以”—–BEGIN…”开头, “—–END…”结尾,内容是BASE64编码.
    查看PEM格式证书的信息:openssl x509 -in certificate.pem -text -noout Apache和*NIX服务器偏向于使用这种编码格式.

  • DER - Distinguished Encoding Rules,打开看是二进制格式,不可读. 查看DER格式证书的信息:openssl x509 -in certificate.der -inform der -text -noout 。
    Java和Windows服务器偏向于使用这种编码格式.

3,OpenSSL建立HTTPS链接最终失败

按照《TLS完全指南(二):OpenSSL操作指南》文档操作,最终失败。
在浏览器输入"https://localhost/",发现提示为"访问 localhost 的请求遭到拒绝 您未获授权,无法查看此网页。HTTP ERROR 403"。

在linux下使用,提示原因如下。辉哥认为自认证产生的证书不可使用。

[root@iZ23prr3ucfZ server]# curl --cacert cert.pem  https://localhost
curl: (60) Peer certificate cannot be authenticated with known CA certificates
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

放弃该篇文章方法尝试。

4, tls-gen产生私钥建立HTTPS链接

4.1 构建 CA 证书链

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

在linux系统 shell 里面执行一下的命令:

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

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

testca/
cacert.pem

server/
cert.pem
key.pem

这是成功的命令输出结果:
[root@iZ23prr3ucfZ tmp]# git clone https://github.com/michaelklishin/tls-gen
Initialized empty Git repository in /root/tmp/tls-gen/.git/
remote: Enumerating objects: 369, done.
remote: Total 369 (delta 0), reused 0 (delta 0), pack-reused 369
Receiving objects: 100% (369/369), 90.46 KiB | 117 KiB/s, done.
Resolving deltas: 100% (215/215), done.
[root@iZ23prr3ucfZ tmp]# ls
server.crt  server.csr  server.go  server.key  tls-gen
[root@iZ23prr3ucfZ tmp]# cd tls-gen/basic
[root@iZ23prr3ucfZ basic]# make CN=localhost
python3 profile.py regenerate --password "" \
    --common-name localhost \
    --client-alt-name iZ23prr3ucfZ \
    --server-alt-name iZ23prr3ucfZ \
    --days-of-validity 3650 \
    --key-bits 2048 
Removing /root/tmp/tls-gen/basic/testca
Removing /root/tmp/tls-gen/basic/result
Removing /root/tmp/tls-gen/basic/server
Removing /root/tmp/tls-gen/basic/client
Will generate a root CA and two certificate/key pairs (server and client)
=>  [openssl_req]
Generating a 2048 bit RSA private key
..................................+++
.............................................................+++
writing new private key to '/root/tmp/tls-gen/basic/testca/private/cakey.pem'
-----
=>  [openssl_x509]
Will generate leaf certificate and key pair for server
Using localhost for Common Name (CN)
Using parent certificate path at /root/tmp/tls-gen/basic/testca/cacert.pem
Using parent key path at /root/tmp/tls-gen/basic/testca/private/cakey.pem
Will use RSA...
=>  [openssl_genrsa]
Generating RSA private key, 2048 bit long modulus
............................................+++
...............................................................................................+++
e is 65537 (0x10001)
=>  [openssl_req]
=>  [openssl_ca]
Using configuration from /tmp/tmpdaabupsr
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :PRINTABLE:'localhost'
organizationName      :PRINTABLE:'server'
localityName          :T61STRING:'$$$$'
Certificate is to be certified until May  6 06:24:49 2029 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
Will generate leaf certificate and key pair for client
Using localhost for Common Name (CN)
Using parent certificate path at /root/tmp/tls-gen/basic/testca/cacert.pem
Using parent key path at /root/tmp/tls-gen/basic/testca/private/cakey.pem
Will use RSA...
=>  [openssl_genrsa]
Generating RSA private key, 2048 bit long modulus
..............+++
............+++
e is 65537 (0x10001)
=>  [openssl_req]
=>  [openssl_ca]
Using configuration from /tmp/tmpdaabupsr
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :PRINTABLE:'localhost'
organizationName      :PRINTABLE:'client'
localityName          :T61STRING:'$$$$'
Certificate is to be certified until May  6 06:24:49 2029 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
Done! Find generated certificates and private keys under ./result!
python3 profile.py verify
Will verify generated certificates against the CA...
Will verify client certificate against root CA
/root/tmp/tls-gen/basic/result/client_certificate.pem: OK
Will verify server certificate against root CA
/root/tmp/tls-gen/basic/result/server_certificate.pem: OK

4.2 编写服务端程序

Go 对 TLS 的支持还是比较完备。以下是服务器端的代码(server.go):

 package main

 import (
    "log"
    "net/http"
 )

 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会用加密的方式来传输。

运行服务程序:

go run server.go

4.3 外部读取文件访问HTTPs服务

编写客户端程序(client.go):

package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "strings"
)

func main() {
    client := &http.Client{}
    resp, err := client.Get("https://localhost: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

如果是WINDOWS,则在浏览器上把"testca/cacert.pem"导入到“受信任的根证书颁发机构”。


现在我们再次运行刚才那个程就会成功的获得服务端的响应了:

This is an example server.

在浏览器上输入https://localhost:1443/hello可以有正常输出:

4.4 代码读取文件访问HTTPs服务

修改client.go文件为直接读取PEM文件的方式。

package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "strings"
    "crypto/x509"
    "crypto/tls"
)

func main() {

    rootPEM, err := ioutil.ReadFile("testca/cacert.pem")
    if err != nil {
        fmt.Print(err)
    }

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

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

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

    fmt.Println(s)
}

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

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

This is an example server.

5. POSTMAN支持https请求

关闭SSL校验,在File -> Setting -> General,关闭"SSL certificate verification",不要如其他文章一样在"Certificates"页面选择证书文件,即可有响应。

6. 参考

(1)Go代码打通HTTPs
https://studygolang.com/articles/12401
(2)TLS完全指南(一):TLS和安全通信
https://zhuanlan.zhihu.com/p/26684050
TLS完全指南(二):OpenSSL操作指南[实际操作失败]
https://zhuanlan.zhihu.com/p/26684071
TLS完全指南(三):用Go语言写HTTPS程序
https://zhuanlan.zhihu.com/p/26684081
(3)Go实战--golang中使用HTTPS以及TSL(.crt、.key、.pem区别以及crypto/tls包介绍)[GITHUB]
https://blog.csdn.net/wangshubo1989/article/details/77508738


本工程文件存放在辉哥的知识星球。加入知识星球后也可以拉入技术讨论群,欢迎加入:https://t.zsxq.com/EiyNbqB

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容

  • HTTPS介绍 超文本传输安全协议(英语:Hypertext Transfer Protocol Secure,缩...
    齐滇大圣阅读 8,914评论 8 96
  • CA和证书安全协议(SSL/TLS)OpenSSH 一、CA和证书 (一) PKI(Public Key Infr...
    哈喽别样阅读 1,387评论 0 0
  • 1 概述 本文之所以称之为半自动化,是因为证书的申请并非日常工作,只是一段时间才需要申请,同时,在创建证书和办法证...
    ghbsunny阅读 2,147评论 0 1
  • 在医院挂水,今天第二天各种检查没停。 认识了临床的老奶奶。我叫她鱼奶奶吧。 一开始我并不认识她,因为她大多时候在睡...
    巴黎小青蛙阅读 211评论 0 1
  • 我起初以为自己很洒脱,许多事情只有要这么做或者不这么做两种方式。 小的时候就不喜欢吃绿色蔬菜,为此父亲经常训我,说...
    涵霸霸阅读 178评论 1 2