?? 从golang 1.15开始废弃 CommonName,因此推荐使用 SAN 证书。 如果想兼容之前的方式,需要设置环境变量 GODEBUG 为 x509ignoreCN=0。
“x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0”.
生成CA根证书
$ openssl genrsa -out ca.key 2048
Generating RSA private key, 2048 bit long modulus
..............................................................................................................................+++
................+++
e is 65537 (0x10001)
$ openssl req -new -x509 -days 3650 -key ca.key -out ca.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) []:cn
State or Province Name (full name) []:shanghai
Locality Name (eg, city) []:shanghai
Organization Name (eg, company) []:golang
Organizational Unit Name (eg, section) []:https
Common Name (eg, fully qualified host name) []:localhost
Email Address []:
$ ls
ca.key ca.pem
配置openssl
默认安装的 OpenSSL 配置文件路径如下:
- linux系统在 : /etc/pki/tls/openssl.cnf
- Mac系统在: /System/Library/OpenSSL/openssl.cnf
- Windows:安装目录下 openssl.cfg,比如 D:\Program Files\OpenSSL-Win64\bin\openssl.cfg
拷贝配置文件到当前目录下,然后修改如下:
- 找到 [ CA_default ],打开 copy_extensions = copy
- 找到[ req ],打开 req_extensions = v3_req # The extensions to add to a certificate request
- 找到[ v3_req ],添加 subjectAltName = @alt_names
- 添加新的标签 [ alt_names ] , 和标签字段
[ alt_names ]
DNS.1 = localhost
DNS.2 = *.custer.fun
这里填入需要加入到 Subject Alternative Names 段落中的域名名称,可以写入多个。
生成服务端证书 - Mac环境
$ openssl genpkey -algorithm RSA -out server.key
..................................+++
.............................................+++
$ openssl req -new -nodes -key server.key -out server.csr -days 3650 -subj "/C=cn/OU=https/O=golang/CN=localhost" -config ./openssl.cnf -extensions v3_req
$ openssl x509 -req -days 3650 -in server.csr -out server.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
Signature ok
subject=/C=cn/OU=https/O=golang/CN=localhost
Getting CA Private Key
$ ls
ca.key ca.pem ca.srl openssl.cnf server.csr server.key server.pem
- server.csr: 是上面生成的CA证书的请求文件,ca.pem/ca.key是CA证书文件和私钥,用来对server.csr进行签名认证
- server.key: 服务端私钥
- server.pem: 经过CA证书签名后的服务端证书
- ca.srl: 是CA使用的序列号文件,当使用"-CA"选项来签名时,它将会使用某个文件中指定的序列号来唯一标识此次签名后的证书文件。这个序列号文件的内容仅只有一行,这一行的值为16进制的数字,当某个序列号被使用后,该文件中的序列号将自动增加。
生成客户端证书 - 同理服务端
$ openssl genpkey -algorithm RSA -out client.key
...........................+++
.....+++
$ openssl req -new -nodes -key client.key -out client.csr -days 3650 -subj "/C=cn/OU=https/O=golang/CN=localhost" -config ./openssl.cnf -extensions v3_req
$ ls
ca.key ca.pem ca.srl client.csr client.key openssl.cnf server.csr server.key server.pem
$ cat ca.srl
CE137EED81F35C23
$ openssl x509 -req -days 3650 -in client.csr -out client.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
Signature ok
subject=/C=cn/OU=https/O=golang/CN=localhost
Getting CA Private Key
$ cat ca.srl
CE137EED81F35C24
$ ls
ca.key ca.pem ca.srl client.csr client.key client.pem openssl.cnf server.csr server.key server.pem
Demo 测试
-
双向认证 -
client
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
clientCertFile := "client.pem"
clientKeyFile := "client.key"
caCertFile := "ca.pem"
var cert tls.Certificate
var err error
if clientCertFile != "" && clientKeyFile != "" {
cert, err = tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
if err != nil {
fmt.Printf("error creating x509 keypair from client cert file %s and client key file %s", clientCertFile, clientKeyFile)
}
}
caCert, err := ioutil.ReadFile(caCertFile)
if err != nil {
fmt.Printf("Error opening cert file %s, Error: %s", caCertFile, err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8869")
if err != nil {
fmt.Println("error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(body))
}
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hi, This is an example of https service in golang!")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServeTLS(":8869", "server.pem", "server.key", nil)
}
curl -kv https://localhost:8869
查看证书内容
使用cat指令也能查看,但信息不够全面
$ openssl x509 -in ca.pem -text
参考
|