生成CA证书
创建CA私钥
openssl genrsa -out ca.key 2048
请求CA证书
openssl req -new -sha256 -key ca.key -out ca.csr -subj "/C=CN/O=Private/CN=CA"
C-----国家(Country Name)
ST----省份(State or Province Name)
L----城市(Locality Name)
O----公司(Organization Name)
OU----部门(Organizational Unit Name)
CN----产品名(Common Name)
emailAddress----邮箱(Email Address)
自签署CA证书
openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.cer
生成服务器证书
创建服务器私钥
openssl genrsa -out server.key 2048
请求服务器证书
openssl req -new -sha256 -key server.key -out server.csr -subj "/C=CN/CN=SERVER" -addext "subjectAltName=DNS:www.mylocal.com,DNS:localhost"
使用CA证书签署服务器证书
openssl x509 -req -days 36500 -sha256 -extensions v3_req -extfile openssl.cnf -CA ca.cer -CAkey ca.key -CAserial ca.srl -CAcreateserial -in server.csr -out server.cer
cat openssl.cnf
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.mylocal.com
DNS.2 = localhost
如果不加-extfile openssl.cnf,会报Warning: ignoring -extensions option without -extfile; 并且签署的证书没有subjectAltName部分。
测试证书
服务器
openssl s_server -CAfile ca.cer -cert server.cer -key server.key -accept 127.0.0.1:443
客户端
openssl s_client -CAfile ca.cer -connect 127.0.0.1:443
查看证书内容命令
openssl req -text -noout -in server.csr
openssl x509 -text -noout -in server.cer
服务器程序
package main
import (
"bufio"
"crypto/tls"
"log"
"net"
)
func main() {
cert, err := tls.LoadX509KeyPair("server.cer", "server.key")
if err != nil {
log.Println(err)
return
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
ln, err := tls.Listen("tcp", "www.mylocal.com:443", config)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
log.Println(err)
return
}
log.Println("receive: " + msg)
n, err := conn.Write([]byte("world\n"))
if err != nil {
log.Println(n, err)
return
}
}
}
客户端程序
package main
import (
"crypto/tls"
"crypto/x509"
"log"
)
func printPeerCrt(conn *tls.Conn) {
certChain := conn.ConnectionState().PeerCertificates
for i, cert := range certChain {
log.Println(i)
log.Println("Issuer: ", cert.Issuer)
log.Println("Subject: ",cert.Subject)
log.Println("Version: ", cert.Version)
log.Println("NotAfter: ", cert.NotAfter)
log.Println("DNS names: ", cert.DNSNames)
log.Println("")
}
}
func main() {
//ca.cer或者server.cer的客户端验证,golang 的tls包两者都支持。
const rootCA = `
-----BEGIN CERTIFICATE-----
MIIC4TCCAckCFH8i2PdtFFFQuOWwuImuPTT1fzE/MA0GCSqGSIb3DQEBCwUAMCwx
CzAJBgNVBAYTAkNOMRAwDgYDVQQKDAdQcml2YXRlMQswCQYDVQQDDAJDQTAgFw0y
NDEwMDkwNjAwNThaGA8yMTI0MDkxNTA2MDA1OFowLDELMAkGA1UEBhMCQ04xEDAO
BgNVBAoMB1ByaXZhdGUxCzAJBgNVBAMMAkNBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAxlwyx9VJsX/IdaLgKuuJpjKGBfMa42JLIzSoZMIA08PdGJAg
UD4uhut9ikuUZLC60qRko75RvnpvV8Rgj9b9l1HdlQ8bKq7FcumfCN9UN9DgbMHv
d6zLCFirZecUWejN+jxCJtd65uqb3KpjCaEcPf2VYy7as0wudJ0SWqXBWx8AuSiQ
7wJCTX+CnnoEK7HCY3/XFe0OG7qRiM47LiAPmXvOncAXkcYmM9+IHjVmPRpm74Nf
USPElWOC88QDTP+Qfuhcrzka4r+RFuoNFlXlkNzDlpB51bt5rAfw+VcR30CT3qD7
25EvVBX7XIU9Yer288eVX1dqmlqijrgK99p6fwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQBbR2+TxtspFu+e0/WwJDO4lzHxRceiVYVg4Pf7llLM219bcSh7horhmK/W
3CVsPnk4VZF5XPwyacMQUsZT3G9qK4g+Psdl9FLsgEiqFSjnKvvRsH9z2w48G0ef
6DV4LdsR9Z3W0S2gD2To1E65Un5lM8Pxfi0E04wFB3j+k9LLMuX4sxGRyJjnzWsU
opP7bY/YMG2RdyDOflEW4dbf65rm3trZBQPihxeQsD3Ogcqjuq4bFLtRkIimjSXU
7RRKDrKV/FH91ZH2TJxZ7Ut6xJWb31QvER6Fj05Z7wTEfYlV6tU/DyY6qPWx+WGU
h+n4/JKJ5qpEYcZmElGklMqIIVfM
-----END CERTIFICATE-----`
const rootServer = `
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIUXbyWdGLu10nO9c25tOBnn+3KBC4wDQYJKoZIhvcNAQEL
BQAwLDELMAkGA1UEBhMCQ04xEDAOBgNVBAoMB1ByaXZhdGUxCzAJBgNVBAMMAkNB
MCAXDTI0MTAwOTA2MDA1OVoYDzIxMjQwOTE1MDYwMDU5WjAeMQswCQYDVQQGEwJD
TjEPMA0GA1UEAwwGU0VSVkVSMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAzVI0yWfET+QGXUF+tGcxGkTA/8MpuKKC38EjglomkK+HKuQy1wIfJmdcLYfF
0RW3459zm6FLUd1ol2vw0h7lbYoGwdbWKHXVNeZpTD6enN7QxaNASamYfMTjCMvi
HDoLO51TxRnxZWQ5sfn5iKuVBgP2VBEz2gYQQPgZNa+VRLMraWEcB6ASDqNK/gbb
dVBRlA5pbyUnNvQPglvyEKxcN69OSVE6kMccXkk6EJrw/kjqScDI2NDTx8W+skLZ
uZxHujIWIuObVtN3NDxKGfjmrXbdWyIrNKXnww0GI8kCfqPPcL1MdRqDYW2Ds4F8
8ocTIej09aSQO+rkJVf1e7UeGwIDAQABo4GcMIGZMCUGA1UdEQQeMByCD3d3dy5t
eWxvY2FsLmNvbYIJbG9jYWxob3N0MB0GA1UdDgQWBBQDICO/gc5QbfqhqMJB9xdF
0JDvJjBRBgNVHSMESjBIoTCkLjAsMQswCQYDVQQGEwJDTjEQMA4GA1UECgwHUHJp
dmF0ZTELMAkGA1UEAwwCQ0GCFH8i2PdtFFFQuOWwuImuPTT1fzE/MA0GCSqGSIb3
DQEBCwUAA4IBAQC14Vz+tuMfC9KbzzhSxIrcuZ3Y+eGlo0nyt7Lnk4dZRMVKVbjW
m4rUXs8vxJWBN+EzqcNtjpUTVFjskzVC/Zk8Iqv1UHoYmlv0yr0wUWDaBWJ/yMlB
leO0rRzBOnfkKCAJFSDczfiyqOf77ehaO5YS0Oce9zzv+cPfehywg5RBlFKfAUdg
g3s8ZyJCam9XIvXyFq2e6zwMJkCR6oYJECOKxvonwq02VydAtXAf6srHY7AJsa47
5+gmwHAn0nv/Ga/QfkVQwswwFTTgNIEdCiM6j4yH1YvSalV5pOEjIFwR+vYlMfct
39OVgzcZObP9NYnkB0A8EAjGRlzbmTn4VZUE
-----END CERTIFICATE-----`
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootServer))
//ok := roots.AppendCertsFromPEM([]byte(rootCA))
if !ok {
panic("failed to parse root certificate")
}
//regard to IP SANs
//conn, err := tls.Dial("tcp", "127.0.0.1:443",
//regard to SANs
//conn, err := tls.Dial("tcp", "www.mylocal.com:443",
conn, err := tls.Dial("tcp", "localhost:443",
&tls.Config{InsecureSkipVerify: false,
RootCAs: roots,
MaxVersion: tls.VersionTLS12,
})
if err != nil {
log.Fatal(err)
}
printPeerCrt(conn)
defer conn.Close()
_, err = conn.Write([]byte("hello\n"))
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1000)
n, err := conn.Read(buf)
if err != nil {
log.Fatal(err)
}
log.Println("receive: " + string(buf[:n]))
}