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) }