go:https 客户端 服务端 demo-尊龙官方平台

go:https 客户端 服务端 demo

el/2024/3/25 16:40:42

go:https 客户端 & 服务端 demo


1.运行环境

[test1280@test1280 ~]$ cat /proc/version 
linux version 2.6.32-642.el6.x86_64 (mockbuild@worker1.bsys.centos.org) (gcc version 4.4.7 20120313 (red hat 4.4.7-17) (gcc) ) #1 smp tue may 10 17:27:01 utc 2016
[test1280@test1280 ~]$ cat /etc/redhat-release 
centos release 6.8 (final)
[test1280@test1280 ~]$ go version
go version go1.13.6 linux/amd64

2.https服务端 客户端单向鉴权认证

创建https服务端,需要先向ca申请服务端证书。

由于申请ca证书需要交纳费用,因此我们创建自己的ca,用自建ca签发证书给自己的服务端。

当然,客户端访问时,需要将自建ca添加到客户端ca信任列表中。

2.1.自建ca

$ openssl genrsa -out rootca.key 2048

$ openssl req -x509 -new -nodes -key rootca.key -days 365 -out rootca.pem

[test1280@test1280 https-server]$ openssl genrsa -out rootca.key 2048
generating rsa private key, 2048 bit long modulus
....................   
........................................   
e is 65537 (0x10001)
[test1280@test1280 https-server]$ openssl req -x509 -new -nodes -key rootca.key -days 365 -out rootca.pem
you are about to be asked to enter information that will be incorporated
into your certificate request.
what you are about to enter is what is called a distinguished name or a dn.
there are quite a few fields but you can leave some blank
for some fields there will be a default value,
if you enter '.', the field will be left blank.
-----
country name (2 letter code) [xx]:
state or province name (full name) []:
locality name (eg, city) [default city]:
organization name (eg, company) [default company ltd]:
organizational unit name (eg, section) []:
common name (eg, your name or your server's hostname) []:test1280'ca
email address []:

其中,rootca.key是我们ca的私钥,rootca.pem是我们ca的证书。

2.2.签发证书

使用自建的ca,签发服务端证书。

$ openssl genrsa -out server.key 2048

$ openssl req -new -key server.key -out server.csr

$ openssl x509 -req -in server.csr -ca rootca.pem -cakey rootca.key -cacreateserial -out server.crt -days 365

[test1280@test1280 https-server]$ openssl genrsa -out server.key 2048
generating rsa private key, 2048 bit long modulus
.   
.   
e is 65537 (0x10001)
[test1280@test1280 https-server]$ openssl req -new -key server.key -out server.csr
you are about to be asked to enter information that will be incorporated
into your certificate request.
what you are about to enter is what is called a distinguished name or a dn.
there are quite a few fields but you can leave some blank
for some fields there will be a default value,
if you enter '.', the field will be left blank.
-----
country name (2 letter code) [xx]:
state or province name (full name) []:
locality name (eg, city) [default city]:
organization name (eg, company) [default company ltd]:
organizational unit name (eg, section) []:
common name (eg, your name or your server's hostname) []:test1280
email address []:please enter the following 'extra' attributes
to be sent with your certificate request
a challenge password []:
an optional company name []:
[test1280@test1280 https-server]$ openssl x509 -req -in server.csr -ca rootca.pem -cakey rootca.key -cacreateserial -out server.crt -days 365
signature ok
subject=/c=xx/l=default city/o=default company ltd/cn=test1280
getting ca private key

其中,server.key是服务端私钥,server.crt是由自建ca签名发布的证书。

注意:在生成server.csr(certificate signing request)时,主机名填写的是test1280(后续客户端访问将用到)。

2.3.https服务端

package mainimport ("log""net/http"
)func main() {http.handlefunc("/", func (w http.responsewriter, r *http.request) {w.write([]byte("http/1.1 with tls/ssl"))})log.fatal(http.listenandservetls(":1280", "server.crt", "server.key", nil))
}

在http.listenandservetls时,设置服务端的私钥以及由自建ca签发的服务端证书。

2.4.https客户端,直接信任服务证书

在单向tls/ssl通信时,客户端建立链路到服务端,服务端发送其证书给客户端。

客户端解析服务端证书,找到签发服务端证书的ca,并从客户端侧信任ca列表找出此ca证书,进行校验:

  • 服务端证书的签发者ca,是否在客户端的ca可信列表中?

  • 若服务端证书签发者ca,已经在客户端的ca可信列表中,服务端证书是否有被篡改过?

  • ……等。

如果校验通过,因为客户端信任ca,ca签发(信任)服务端,因此客户端信任服务端=>【信任链】

关闭客户端对服务端证书的校验,无条件信任服务端发来的证书,客户端代码如下:

package mainimport ("crypto/tls""io/ioutil""log""net/http"
)func main() {tr := &http.transport{tlsclientconfig: &tls.config{/* 不校验服务端证书,直接信任 */insecureskipverify: true,},}client := &http.client{transport: tr,}/* 协议:https,非http */resp, err := client.get("https://127.0.0.1:1280/")if err != nil {log.fatalln(err)}defer resp.body.close()body, err := ioutil.readall(resp.body)if err != nil {log.fatalln(err)}log.printf("%s\n", body)
}

特别注意:设置 insecureskipverify: true

编译 & 运行:

# 服务端
[test1280@test1280 https-server]$ go run main.go 
^csignal: interrupt# 客户端
[test1280@test1280 https-client]$ go run main.go 
2020/12/08 05:31:43 http/1.1 with tls/ssl

2.5.https客户端,校验服务端证书

在 2.4 小节中列出客户端使用 https,但不校验服务端证书的 demo,实际上极不安全的行为。

“极不安全行为”,指对客户端而言不安全,客户端无法识别,对面的“警察”,是否是真警察,还是诈骗犯冒充。

以下是客户端使用 https,并校验服务端证书的 demo。

先添加内网私有域名test1280到 /etc/hosts 中,或者私有内网dns域名服务器。

注意:

如果使用 /etc/hosts 注入私有域名映射,应设置到客户端主机中。此处客户端、服务端均在同一主机。

[root@test1280 ~]# echo "127.0.0.1 test1280" >> /etc/hosts

这里的内网域名,test1280,即服务端的域名,也是在生成server.csr时填入的域名。

客户端访问服务端时,必须通过 test1280 域名访问,而不能是 ip 地址(或其他域名)。

客户端校验服务端证书代码如下:

package mainimport ("crypto/tls""crypto/x509""io/ioutil""log""net/http"
)func main() {// 读取ca证书cacrt, err := ioutil.readfile("rootca.pem")if err != nil {log.fatalln(err)}// 创建ca证书池capool := x509.newcertpool()// 添加ca证书到池capool.appendcertsfrompem(cacrt)tr := &http.transport{tlsclientconfig: &tls.config{/* 设置ca证书池,池里的ca证书,均为可信的 */rootcas: capool,},}client := &http.client{transport: tr,}/* 协议:https,非http */resp, err := client.get("https://test1280:1280/")if err != nil {log.fatalln(err)}defer resp.body.close()body, err := ioutil.readall(resp.body)if err != nil {log.fatalln(err)}log.printf("%s\n", body)
}

注意:

  • 客户端一侧,将自建的ca证书(签发服务端证书的ca),加载到客户端代码中;

  • 客户端访问服务端时,不再是通过 ip 访问,而是通过 https://test1280:port/path 的域名形式访问。

编译 & 运行:

[test1280@test1280 https-client]$ go run main.go 
2020/12/08 06:38:25 http/1.1 with tls/ssl

总结:

1.服务端生成私钥,并通过自建ca,签名生成服务端证书。

2.服务端在启动时,设置私钥,以及服务端证书(下发给客户端校验)。

3.客户端在启动时,需要将自建ca证书加入到客户端信任ca列表,用以后续校验对面的警察是否是真警察。

如上,完成客户端与服务端使用tls/ssl通信,且客户端对服务端单向校验认证。

3.https服务端 客户端双向鉴权认证

在银行等金融体系中,不仅保证客户端安全(让客户端能够鉴别服务端真假)之外,服务端还会校验客户端身份,保证没有人冒充客户端进行敏感操作。

因此常使用 tls/ssl 双向鉴权认证:

  • 服务端提供证书,客户端校验,客户端识别服务端的真伪。

  • 客户端提供证书,服务端校验,服务端认证客户端的权限。不合法(如没有客户端证书)的客户端建链失败,后续敏感操作失败。

参照客户端对服务端的鉴权认证,我们需要:

  • 自建一个ca
  • 用此ca签发客户端证书
  • 将此ca加入到服务端信任ca列表
  • 客户端访问服务端时,将客户端证书发给服务端校验

3.1.自建客户端ca

$ openssl genrsa -out clientrootca.key 2048

$ openssl req -x509 -new -nodes -key clientrootca.key -days 365 -out clientrootca.pem

[test1280@test1280 https-client]$ openssl genrsa -out clientrootca.key 2048
generating rsa private key, 2048 bit long modulus
.........................................................................   
..........   
e is 65537 (0x10001)
[test1280@test1280 https-client]$ openssl req -x509 -new -nodes -key clientrootca.key -days 365 -out clientrootca.pem
you are about to be asked to enter information that will be incorporated
into your certificate request.
what you are about to enter is what is called a distinguished name or a dn.
there are quite a few fields but you can leave some blank
for some fields there will be a default value,
if you enter '.', the field will be left blank.
-----
country name (2 letter code) [xx]:
state or province name (full name) []:
locality name (eg, city) [default city]:
organization name (eg, company) [default company ltd]:
organizational unit name (eg, section) []:
common name (eg, your name or your server's hostname) []:test1280'ca
email address []:

其中,clientrootca.key是自建ca(客户端侧)的私钥,clientrootca.pem是其证书。

3.2.签发客户端证书

$ openssl genrsa -out client.key 2048

$ openssl req -new -key client.key -out client.csr

$ openssl x509 -req -in client.csr -ca clientrootca.pem -cakey clientrootca.key -cacreateserial -out client.crt -days 365

[test1280@test1280 https-client]$ openssl genrsa -out client.key 2048
generating rsa private key, 2048 bit long modulus
.......................   
..........   
e is 65537 (0x10001)
[test1280@test1280 https-client]$ openssl req -new -key client.key -out client.csr
you are about to be asked to enter information that will be incorporated
into your certificate request.
what you are about to enter is what is called a distinguished name or a dn.
there are quite a few fields but you can leave some blank
for some fields there will be a default value,
if you enter '.', the field will be left blank.
-----
country name (2 letter code) [xx]:
state or province name (full name) []:
locality name (eg, city) [default city]:
organization name (eg, company) [default company ltd]:
organizational unit name (eg, section) []:
common name (eg, your name or your server's hostname) []:
email address []:please enter the following 'extra' attributes
to be sent with your certificate request
a challenge password []:
an optional company name []:
[test1280@test1280 https-client]$ openssl x509 -req -in client.csr -ca clientrootca.pem -cakey clientrootca.key -cacreateserial -out client.crt -days 365
signature ok
subject=/c=xx/l=default city/o=default company ltd
getting ca private key

其中,client.key是客户端私钥,client.crt是客户端证书(由clientrootca.pem签发)。

3.3.服务端 demo

[test1280@test1280 https-server]$ tree .
.
├── clientrootca.pem
├── go.mod
├── main.go
├── server.crt
└── server.key0 directories, 5 files
package mainimport ("crypto/tls""crypto/x509""io/ioutil""log""net/http"
)func main() {// 读取签发客户端证书的ca证书cacrt, err := ioutil.readfile("clientrootca.pem")if err != nil {log.fatalln(err)}// 创建用于信任校验客户端的ca证书池capool := x509.newcertpool()// 添加ca证书到池(信任签发客户端证书的ca)capool.appendcertsfrompem(cacrt)server := &http.server{addr:    ":1280",handler: &myhandler{},tlsconfig: &tls.config{// 设置服务端对客户端的ca信任池clientcas:  capool,// 设置tls/ssl鉴权认证时要求客户端提供其证书并校验clientauth: tls.requireandverifyclientcert,},}// 设置:// * 服务端要发送给客户端认证的服务端证书// * 服务端自己的私钥,用以和客户端协商对称加解密密钥时的加解密log.fatal(server.listenandservetls("server.crt", "server.key"))
}type myhandler struct {
}func (*myhandler) servehttp(w http.responsewriter, r *http.request) {w.write([]byte("http/1.1 tls/ssl 双向校验"))
}

编译 & 启动:

[test1280@test1280 https-server]$ go run main.go 
^csignal: interrupt

3.4.客户端 demo

[test1280@test1280 https-client]$ tree .
.
├── client.crt
├── client.key
├── go.mod
├── main.go
└── rootca.pem0 directories, 5 files
package mainimport ("crypto/tls""crypto/x509""io/ioutil""log""net/http"
)func main() {// 读取签发服务端证书的ca证书cacrt, err := ioutil.readfile("rootca.pem")if err != nil {log.fatalln(err)}// 创建用于信任校验服务端的ca证书池capool := x509.newcertpool()// 添加ca证书到池(信任签发服务端证书的ca)capool.appendcertsfrompem(cacrt)// 读取客户端的证书,以及客户端私钥clientcrt, err := tls.loadx509keypair("client.crt", "client.key")if err != nil {log.fatalln(err)}tr := &http.transport{tlsclientconfig: &tls.config{/* 设置服务端ca证书池,池里的ca证书,均为可信的 */rootcas: capool,/* 设置要发送给服务端校验的客户端证书;和客户端自用私钥(不发给服务端) */certificates: []tls.certificate{clientcrt,},},}client := &http.client{transport: tr,}/* 协议:https,非http */resp, err := client.get("https://test1280:1280/")if err != nil {log.fatalln(err)}defer resp.body.close()body, err := ioutil.readall(resp.body)if err != nil {log.fatalln(err)}log.printf("%s\n", body)
}

编译 & 启动:

[test1280@test1280 https-client]$ go run main.go 
2020/12/08 07:24:36 http/1.1 tls/ssl 双向校验

4.faq

4.1.每建一个客户端证书,都需新建一个ca吗?

ca其实是中立的一个信用中心,通过一个ca可以签发很多证书给客户端、服务端使用。

在上例的客户端服务端双向tls/ssl认证时,客户端单独创建了一个ca,与签发服务端的ca不是同一个ca实例。

但实际完全可以用同一个ca,既签发客户端证书,又签发服务端证书。

在上例的客户端、服务端使用不同的ca,只是想表述,这两个ca可以不同,但是实际可以相同。


5.参考

  • https://github.com/golang/net/tree/master/http2/h2demo
  • https://colobu.com/2016/06/07/simple-golang-tls-examples/
  • https://tonybai.com/2015/04/30/go-and-https/

http://www.ngui.cc/el/5127037.html

相关文章

go:go对linux内核版本要求

go:go对linux内核版本要求 1.结论 go对linux内核版本最低要求是 2.6.23,对应要求操作系统版本是: rhel 6.0centos 6.0 即,不支持 (rhel 和 centos) 的 (4.x or 5.x)。 the go runtime requires linux kernel version 2.6.23 h…

linux:生成core的几种方式

linux:生成core的几种方式 1.总结 在某些情况下,进程会生成core文件(核心转储),记录进程状态,帮助我们快速定位异常。 例如: 当进程异常时如段错误退出,可以分析结果core…

go:panic时core的生成(gotraceback)与调试

go:panic时core的生成(gotraceback)与调试 1.需求 基于go实现的application在异常panic时进程将退出,并在终端输出panic信息。 例如: package mainfunc main() {panic("test1280 :(") }[test1280test1280…

c/c :tcp bind error:address already in use

c/c:tcp bind error:address already in use 在编写、运行服务端程序时,经常会遇到的一个错误是:address already in use. address already in use 是在调用bind系统调用时出现的错误。 原因有两个: 1.bind一个已经…

go:disable http chunk mode

go:disable http chunk mode 1.结论 应用层显式设置 content-length,可以 disable chunk mode。 http.handlefunc("/", func(writer http.responsewriter, request *http.request) {writer.header().set("content-length", "2…

go:http transfer-encoding chunked 实时读写

go:http transfer-encoding chunked 实时读写 服务端: package mainimport ("fmt""net/http""time" )func main() {http.handlefunc("/", func(writer http.responsewriter, request *http.request) {flusher,…

go:http request cancelled 服务端感知

go:http request cancelled 服务端感知 1.背景 今天查问题的时候,偶然发现github上一个有意思的问题,记录下来。 原问题:https://github.com/golang/go/issues/23262 2.问题 首先,我们思考一个问题: 当…

go:read一个已经被canceled的http.request的应答

go:read一个已经被canceled的http.request的应答 1.复现 最近发现项目在处理chunk类型的http应答时,出现读数据异常报错,代码示例如下: server package mainimport ("bytes""net/http" )func main() {http…

docker:安装vim

docker:安装vim 安装命令:apt install vim -y 异常错误:unable to locate package vim # apt install vim reading package lists... done building dependency tree reading state information... done e: unable to locate package…

go:zap log rotate(日志轮转)

go:zap log rotate(日志轮转) demo: package mainimport ("go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2" )func main() {// 日志级别loglevel : "debug&qu…
网站地图