一、背景知识
-
密码学的一些基本知识
大致上分为两类,基于key的加密算法与不基于key的加密算法。现在的算法基本都是基于key的,key就以一串随机数数,更换了key之后,算法还可以继续使用。
基于key的加密算法又分为两类,对称加密和不对称加密,比如DES,AES那种的,通信双方一方用key加密之后,另一方用相同的key进行反向的运算就可以解密。
不对称加密比较著名的就是RSA,加密的时候有一个公钥和一个私钥,公钥是可以交给对方的,a给b发送信息,a用自己的私钥加密,b用a的公钥解密,反之,b给a发送信息,b用自己的私钥加密。
在通信之前,需要经过一些握手的过程,双方交换公钥,这个就是key exchange的过程,https最开始的阶段就包含了这个key exchange的过程。
-
数字证书与CA
数字证书相当于是服务器的一个“身份证”,用于唯一标识一个服务器。一般而言,数字证书从受信的权威证书授权机构 (Certification Authority,证书授权机构)买来的(免费的很少),浏览器里面一般就内置好了一些权威的CA,在使用https的时候,只要是这些CA签发的证书,浏览器都是可以认证的,要是在与服务器通信的时候,收到一个没有权威CA认证的证书,就会报出提醒不受信任证书的错误,就像登录12306一样,但是也可以选择接受。
通常是自己签发一个ca根证书,之后这个根证书签发一个server.crt,以及server.key给服务端,server.key是服务端的私钥,server.crt包含了服务端的公钥还有服务端的一些身份信息。在客户端和服务端通信的时候(特别是使用代码编写的客户端访问的时候),要指定ca根证书
三、HPPTS原理
HTTPS是在HTTP下加入SSL(Secure Sockets Layer 安全套接层)层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
SSL,及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
HTTPS由两部分组成:HTTP+SSL/TLS,也就是在HTTP上又加了一层处理加密信息的模块。工作原理图如下:
详细介绍(引用 https://www.cnblogs.com/zhangshitong/p/6478721.html)
三、安装 OpenSSL
openssl 是目前最流行的 SSL 密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持SSL/TLS 协议的实现。官网:https://www.openssl.org/source/,其中有3个主要的用途:1、密码算法库(建立 RSA、DH、DSA key 参数,计算消息摘要,使用各种 Cipher加密/解密) 2、密钥和证书封装管理功能(建立 X.509 证书、证书签名请求(CSR)和CRLs(证书回收列表));3、SSL通信API接口(SSL/TLS 客户端以及服务器的测试,处理S/MIME 或者加密邮件)。
- Windows 安装OpenSSL
编译好的OpenSSL下载地址: http://slproweb.com/products/Win32OpenSSL.html- 下载后进行安装下一步下一步就可以了
- 把安装目录添加到path里;我默认安装到C盘 所以把C:\OpenSSL-Win64\bin 添加到path里;注意要带上bin目录。
- CentOS安装(引用 https://www.cnblogs.com/loleina/p/8418111.html)
四、HTTPS 实例
1、服务端私钥与证书
- 生成证书
openssl genrsa -out ca.key 2048
#这里可以使用 -subj 不用进行交互 当然还可以添加更多的信息
openssl req -x509 -new -nodes -key ca.key -subj "/CN=ld.com" -days 5000 -out ca.crt
- server 代码
1 package main
2
3 import (
4 "fmt"
5 "net/http"
6 )
7
8 func main() {
9
10 http.HandleFunc("/test", handler)
11 // http.ListenAndServe(":9898", nil)
13 http.ListenAndServeTLS(":9898", "cert/ca.crt", "cert/ca.key", nil)
14 }
15
16 func handler(w http.ResponseWriter, r *http.Request) {
17 fmt.Fprintf(w,"测试!")
18 }
- client 代码
Transport是一个对RoundTripper(一个RoundTripper代表一个http事务,给一个请求返回一个响应)的实现,它支持HTTP、HTTPS和HTTP代理。
有关Transport的详解可以阅读Go&Transport,其中TLSClientConfigTLS字段是TLS的配置信息,如果为nil,则采用默认配置。
1 package main
2
3 import (
4 "crypto/tls"
5 "io/ioutil"
6 "log"
7 "net/http"
8 )
9
10 func main() {
11 var (
12 resp *http.Response
13 err error
14 body []byte
15 tr *http.Transport
16 client *http.Client
17 )
18 // 配置tls参数
19 tr = &http.Transport{
20 TLSClientConfig: &tls.Config{
21 InsecureSkipVerify: true,
22 },
23 }
24
25 client = &http.Client{Transport: tr}
26 // 请求接口
27 resp, err = client.Get("https://127.0.0.1:9898/test")
28
29 if err != nil {
30 log.Fatalln("接口请求失败", err)
31 }
32
33 defer resp.Body.Close()
34 // 读取body数据
35 body, err = ioutil.ReadAll(resp.Body)
36
37 log.Println("请求结果", string(body))
39 }
2、对服务端的证书进行校验
- 生成证书
openssl genrsa -out ca.key 2048
#这里可以使用 -subj 不用进行交互 当然还可以添加更多的信息
openssl req -x509 -new -nodes -key ca.key -subj "/CN=ld.com" -days 5000 -out ca.crt
openssl genrsa -out server.key 2048
#这里的/cn可以是必须添加的 是服务端的域名 或者是etc/hosts中的ip别名
openssl req -new -key server.key -subj "/CN=server" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
#查询证书的情况
openssl x509 -in ./server.crt -noout -text
- server 代码
1 package main
2
3 import (
4 "fmt"
5 "net/http"
6 )
7
8 func main() {
9
10 http.HandleFunc("/test", handler)
11 // http.ListenAndServe(":9898", nil)
12
13 http.ListenAndServeTLS(":9898", "cert/server.crt", "cert/server.key", nil)
14 }
15
16 func handler(w http.ResponseWriter, r *http.Request) {
17 fmt.Fprintf(w,"测试")
18 }
- client 代码
1 package main
2
3 import (
4 "crypto/tls"
5 "crypto/x509"
6 "io/ioutil"
7 "log"
8 "net/http"
9 )
10
11 func main() {
12 var (
13 resp *http.Response
14 err error
15 body []byte
16 tr *http.Transport
17 client *http.Client
18 pool *x509.CertPool
19 crt []byte
20 )
21 pool = x509.NewCertPool()
22 // 读取证书
23 crt, err = ioutil.ReadFile("cert/ca.crt")
24 if err != nil {
25 log.Fatalln("读取证书错误", err)
26 }
27 pool.AppendCertsFromPEM(crt)
28
29 tr = &http.Transport{
30 TLSClientConfig: &tls.Config{
31 // InsecureSkipVerify: true,
32 RootCAs: pool,
33 },
34 DisableCompression: true,
35 }
36
37 client = &http.Client{Transport: tr}
38
39 // resp, err = client.Get("https://127.0.0.1:9898/test")
40 resp,err=client.Get("https//server:9898/test")
41 if err != nil {
42 log.Fatalln("接口请求失败", err)
43 }
44
45 defer resp.Body.Close()
46
47 body, err = ioutil.ReadAll(resp.Body)
48
49 log.Println("请求结果", string(body))
50
51 }
3、server/client的双向校验
- 生成证书
openssl genrsa -out ca.key 2048
# 这里可以使用 -subj 不用进行交互 当然还可以添加更多的信息
openssl req -x509 -new -nodes -key ca.key -subj "/CN=ld.com" -days 5000 -out ca.crt
openssl genrsa -out server.key 2048
#这里的/cn可以是必须添加的 是服务端的域名 或者是etc/hosts中的ip别名
openssl req -new -key server.key -subj "/CN=server" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 5000
#查询证书的情况
openssl x509 -in ./server.crt -noout -text
注意生成client端证书的时候,注意要多添加一个字段,golang的server端认证程序会对这个字段进行认证:
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=client" -out client.csr
echo extendedKeyUsage=clientAuth > extfile.cnf
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile extfile.cnf -out client.crt -days 5000
- server代码
package main
// 双向认证
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type ServerHandler struct{}
func (s *ServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"测试")
}
func main() {
pool := x509.NewCertPool()
caPath := "cert/ca.crt"
crt, err := ioutil.ReadFile(caPath)
if err != nil {
log.Fatalln("读取证书错误", err)
}
pool.AppendCertsFromPEM(crt)
s := &http.Server{
Addr: ":9898",
Handler: &ServerHandler{},
TLSConfig: &tls.Config{
ClientCAs: pool,
ClientAuth: tls.RequireAndVerifyClientCert,
},
}
s.ListenAndServeTLS("cert/server.crt", "cert/server.key")
}
- client 代码
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"net/http"
)
func main() {
var (
resp *http.Response
err error
body []byte
tr *http.Transport
client *http.Client
pool *x509.CertPool
crt []byte
cliCrt tls.Certificate
)
pool = x509.NewCertPool()
// 读取证书
crt, err = ioutil.ReadFile("cert/ca.crt")
if err != nil {
log.Fatalln("读取证书错误", err)
}
pool.AppendCertsFromPEM(crt)
// 加载X509 client证书和秘钥
cliCrt,err=tls.LoadX509KeyPair("cert/client.crt","cert/client.key")
if err!=nil{
panic(err)
}
tr = &http.Transport{
TLSClientConfig: &tls.Config{
// InsecureSkipVerify: true,
RootCAs: pool,
Certificates:[]tls.Certificate{cliCrt},
},
// DisableCompression: true,
}
client = &http.Client{Transport: tr}
resp, err = client.Get("https://127.0.0.1:9898/test")
if err != nil {
log.Fatalln("接口请求失败", err)
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
log.Println("请求结果", string(body))
}
四、错误
- cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
说明是认证没有通过,因为客户端这面并没有提供可以信赖的根证书来对服务端发过来的证书进行验,/CN使用的直接是ip地址,就会报下面的错误: