接受客户端处理逻辑:

因为 go 1.15 版本开始废弃 CommonName,因此推荐使用 SAN 证书。 如果想兼容之前的方式,需要设置环境变量 GODEBUG 为 x509ignoreCN=0。(但是设置环境变量似乎无效

客户端读取时报错的具体内容为

2021/10/03 19:27:48 Get "https://localhost:8848": x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0

GO的升级说明 https://golang.org/doc/go1.15#commonname

本问题解决参考链接:

https://www.cnblogs.com/jackluo/p/13841286.html (问题阐述)

https://blog.csdn.net/weixin_40280629/article/details/113563351 (证书生成)

什么是 SAN
SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。

下面简单示例如何用 openssl 生成 ca 和双方 SAN 证书。

老的证书生成流程(在升级版本的GO中已经不支持读取)

使用-subj参数,指定服务器的相关信息,与之前的不同,此时不需要引导输入。
openssl req \
    -x509 \
    -nodes \
    -newkey rsa:2048 \
    -keyout server.key \
    -out server.crt \
    -days 3650 \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=Global Security/OU=IT Department/CN=*"

新的证书生成(使用开启扩展SAN的证书)
(参考https://blog.csdn.net/weixin_40280629/article/details/113563351)
SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。

2.如何生成含有SAN的证书
2.1. 生成CA根证书
2.1.1 准备ca配置文件,得到ca.conf

vim ca.conf

内容如下:

[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName = Locality Name (eg, city)
localityName_default = NanJing
organizationName = Organization Name (eg, company)
organizationName_default = Sheld
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
commonName_default = Ted CA Test

2.1.2 生成ca秘钥,得到ca.key

openssl genrsa -out ca.key 4096

2.1.3 生成ca证书签发请求,得到ca.csr

openssl req \
-new \
-sha256 \
-out ca.csr \
-key ca.key \
-config ca.conf

配置文件中已经有默认值了,shell交互时一路回车就行

2.1.4 生成ca根证书,得到ca.crt

openssl x509 \
-req \
-days 3650 \
-in ca.csr \
-signkey ca.key \
-out ca.crt

2.2. 生成终端用户证书

2.2.1 准备配置文件,得到server.conf

vim server.conf

内容如下: (这里需要指定域名)本案例指定本机localhost

[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName = Locality Name (eg, city)
localityName_default = NanJing
organizationName = Organization Name (eg, company)
organizationName_default = Sheld
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
commonName_default = localhost

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost
IP = 192.168.1.100

2.2.2 生成秘钥,得到server.key

openssl genrsa -out server.key 2048

2.2.3 生成证书签发请求,得到server.csr

openssl req \
-new \
-sha256 \
-out server.csr \
-key server.key \
-config server.conf

配置文件中已经有默认值了,shell交互时一路回车就行

2.2.4 用CA证书生成终端用户证书,得到server.crt

openssl x509 \
-req \
-days 3650 \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-in server.csr \
-out server.pem\
-extensions req_ext \
-extfile server.conf

现在证书已经生成完毕, server.pem 和 server.key正式我们需要的证书和密钥

使用GO语言编写服务端与客户端读取。(程序代码没有改变 主要是证书版本的更新

服务端 server.go

package main

import (
	"fmt"
	"log"
	"net/http"
)

//https单向认证编写

func main()  {
	//创建http server
	server := http.Server{
		Addr: ":8848", //监听端口
		Handler: nil, //使用默认处理器,还是要实现处理逻辑
		TLSConfig: nil,
	}

	//处理逻辑
	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		fmt.Println("HandelFunc called")
		writer.Write([]byte("Hello world"))
	})
	//启动http server
	err := server.ListenAndServeTLS("./server.pem","./server.key")
	if err != nil{
		log.Fatal(err)
	}
}

客户端 client.go

1. 注册给服务器颁发证书的ca
– 读取ca证书
– 把ca的证书添加到ca池中
2. 配置tls
3.创建http client
4. client发起请求
5. 打印返回值

package main

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

func main() {

	//1. 注册给服务器颁发证书的ca
	//- 读取ca证书, 我们的证书是自签名的,server.crt能够认证自己,server.crt当成CA证书
	caCerInfo /*pem格式*/ , err := ioutil.ReadFile("./server.crt")
	if err != nil {
		log.Fatal(err)
	}

	//- 把ca的证书添加到ca池中
	//- 创建ca池
	cerPool := x509.NewCertPool()

	//- 将ca添加到ca池
	cerPool.AppendCertsFromPEM(caCerInfo)

	//
	//2. 配置tls
	// RootCAs defines the set of root certificate authorities
	// that clients use when verifying server certificates.
	// If RootCAs is nil, TLS uses the host's root CA set.
	//RootCAs *x509.CertPool

	//将我们承认ca池配置给tls
	cfg := tls.Config{
		RootCAs: cerPool,
	}

	//fmt.Printf("cfg : %s", cfg)

	//3.创建http client
	client := http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &cfg,
			//TLSClientConfig: nil,
		},
	}

	//4. client发起请求
	response, err := client.Get("https://localhost:8848")
	if err != nil {
		log.Fatal(err)
	}

	//5. 打印返回值
	bodyInfo, err := ioutil.ReadAll(response.Body)

	if err != nil {
		log.Fatal(err)
	}

	//勿忘
	response.Body.Close()

	//body
	fmt.Printf("body : %s\n", bodyInfo)

	//状态码
	fmt.Printf("status code : %s\n", response.Status)
}