前言
最近在学习gRPC,在生成服务端证书与客户端实现双向加密中遇到了证书错误,特此记录下过程及解决方法。
错误
transport: authentication handshake failed: x509: certificate relies on legacy Common Name field, use S
ANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0。从错误来看是因为证书中依赖传统的CN(Common Name)字段,而Go自1.15版本废弃了common name方式认证,需要使用SANS证书进行认证,或者设置Go环境变量GODEBUG=x509ignoreCN=0。
解决
生成CA证书
1
| $ openssl genrsa -out ca.key 4096
|
1
2
3
4
| $ openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/O=example/CN=test.com" \
-key ca.key \
-out ca.crt
|
生成服务端证书
1
| $ openssl genrsa -out server.key 4096
|
1
2
3
4
| $ openssl req -sha512 -new \
-subj "/O=example/CN=test.com" \
-key server.key \
-out server.csr
|
1
2
3
4
5
6
7
8
9
10
11
| $ cat > v3-server.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=test.com
DNS.2=www.test.com
EOF
|
1
2
3
4
5
| $ openssl x509 -req -sha512 -days 3650 \
-extfile v3-server.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in server.csr \
-out server.crt
|
生成客户端证书
1
| $ openssl genrsa -out client.key 4096
|
1
2
3
4
| $ openssl req -sha512 -new \
-subj "/O=example/CN=test.com" \
-key client.key \
-out client.csr
|
1
2
3
4
5
6
7
8
9
10
11
| $ cat > v3-client.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=test.com
DNS.2=www.test.com
EOF
|
该文件与客户端证书唯一不同就在于extendedKeyUsage,服务端是serverAuth,客户端为clientAuth。
1
2
3
4
5
| $ openssl x509 -req -sha512 -days 3650 \
-extfile v3-client.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in client.csr \
-out client.crt
|
应用
拷贝上述生成的ca.crt,server.crt,server.key,client.crt,client.key文件到项目的certs目录中。服务端及客户端代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| cert,err:=tls.LoadX509KeyPair("certs/server.crt","certs/server.key")
if err!=nil{
log.Fatal(err)
}
certPool:=x509.NewCertPool()
ca,err:=ioutil.ReadFile("certs/ca.crt")
if err!=nil{
log.Fatal(err)
}
certPool.AppendCertsFromPEM(ca)
cred:=credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
})
|
1
2
3
4
5
6
7
8
9
10
| cert,_:=tls.LoadX509KeyPair("certs/client.crt","certs/client.key")
certPool:=x509.NewCertPool()
ca,_:=ioutil.ReadFile("certs/ca.crt")
certPool.AppendCertsFromPEM(ca)
cred:=credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: "test.com", //alt_names文件中的任意域名一致
RootCAs: certPool,
})
|