GO实现SAN数字证书https单向认证
接受客户端处理逻辑:
因为 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)
}
