-
-
Save xjdrew/97be3811966c8300b724deabc10e38e2 to your computer and use it in GitHub Desktop.
| package main | |
| import ( | |
| "crypto/tls" | |
| "crypto/x509" | |
| "flag" | |
| "io" | |
| "io/ioutil" | |
| "log" | |
| "os" | |
| "strings" | |
| "sync" | |
| ) | |
| func createClientConfig(ca, crt, key string) (*tls.Config, error) { | |
| caCertPEM, err := ioutil.ReadFile(ca) | |
| if err != nil { | |
| return nil, err | |
| } | |
| roots := x509.NewCertPool() | |
| ok := roots.AppendCertsFromPEM(caCertPEM) | |
| if !ok { | |
| panic("failed to parse root certificate") | |
| } | |
| cert, err := tls.LoadX509KeyPair(crt, key) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &tls.Config{ | |
| Certificates: []tls.Certificate{cert}, | |
| RootCAs: roots, | |
| }, nil | |
| } | |
| func printConnState(conn *tls.Conn) { | |
| log.Print(">>>>>>>>>>>>>>>> State <<<<<<<<<<<<<<<<") | |
| state := conn.ConnectionState() | |
| log.Printf("Version: %x", state.Version) | |
| log.Printf("HandshakeComplete: %t", state.HandshakeComplete) | |
| log.Printf("DidResume: %t", state.DidResume) | |
| log.Printf("CipherSuite: %x", state.CipherSuite) | |
| log.Printf("NegotiatedProtocol: %s", state.NegotiatedProtocol) | |
| log.Printf("NegotiatedProtocolIsMutual: %t", state.NegotiatedProtocolIsMutual) | |
| log.Print("Certificate chain:") | |
| for i, cert := range state.PeerCertificates { | |
| subject := cert.Subject | |
| issuer := cert.Issuer | |
| log.Printf(" %d s:/C=%v/ST=%v/L=%v/O=%v/OU=%v/CN=%s", i, subject.Country, subject.Province, subject.Locality, subject.Organization, subject.OrganizationalUnit, subject.CommonName) | |
| log.Printf(" i:/C=%v/ST=%v/L=%v/O=%v/OU=%v/CN=%s", issuer.Country, issuer.Province, issuer.Locality, issuer.Organization, issuer.OrganizationalUnit, issuer.CommonName) | |
| } | |
| log.Print(">>>>>>>>>>>>>>>> State End <<<<<<<<<<<<<<<<") | |
| } | |
| func main() { | |
| connect := flag.String("connect", "localhost:4433", "who to connect to") | |
| ca := flag.String("ca", "./ca.crt", "root certificate") | |
| crt := flag.String("crt", "./client.crt", "certificate") | |
| key := flag.String("key", "./client.key", "key") | |
| flag.Parse() | |
| addr := *connect | |
| if !strings.Contains(addr, ":") { | |
| addr += ":443" | |
| } | |
| config, err := createClientConfig(*ca, *crt, *key) | |
| if err != nil { | |
| log.Fatal("config failed: %s", err.Error()) | |
| } | |
| conn, err := tls.Dial("tcp", addr, config) | |
| if err != nil { | |
| log.Fatalf("failed to connect: %s", err.Error()) | |
| } | |
| defer conn.Close() | |
| log.Printf("connect to %s succeed", addr) | |
| printConnState(conn) | |
| var wg sync.WaitGroup | |
| wg.Add(1) | |
| go func() { | |
| io.Copy(conn, os.Stdin) | |
| wg.Done() | |
| }() | |
| wg.Add(1) | |
| go func() { | |
| io.Copy(os.Stdout, conn) | |
| wg.Done() | |
| }() | |
| wg.Wait() | |
| } |
| # >>>>>>>>>>>>>>>>>> 根证书 <<<<<<<<<<<<<<<<<<<<<< | |
| # 生成根证书私钥: ca.key | |
| openssl genrsa -out ca.key 2048 | |
| # 生成自签名根证书: ca.crt | |
| openssl req -new -key ca.key -x509 -days 3650 -out ca.crt -subj /C=CN/ST=GuangDong/O="Localhost Ltd"/CN="Localhost Root" | |
| # >>>>>>>>>>>>>>>>>> 服务器证书 <<<<<<<<<<<<<<<<<<<<<< | |
| # 生成服务器证书私钥: ca.key | |
| openssl genrsa -out server.key 2048 | |
| # 生成服务器证书请求: server.csr | |
| openssl req -new -nodes -key server.key -out server.csr -subj /C=CN/ST=GuangDong/L=Guangzhou/O="Localhost Server"/CN=localhost | |
| # 签名服务器证书: server.crt | |
| openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt | |
| # >>>>>>>>>>>>>>>>>> 客户端证书 <<<<<<<<<<<<<<<<<<<<<< | |
| # 生成客户端证书私钥: ca.key | |
| openssl genrsa -out client.key 2048 | |
| # 生成客户端证书请求: client.csr | |
| openssl req -new -nodes -key client.key -out client.csr -subj /C=CN/ST=GuangDong/L=Guangzhou/O="Localhost Client"/CN=localhost | |
| # 签名客户端证书: client.crt | |
| openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt |
| package main | |
| import ( | |
| "crypto/tls" | |
| "crypto/x509" | |
| "flag" | |
| "io" | |
| "io/ioutil" | |
| "log" | |
| "net" | |
| ) | |
| func createServerConfig(ca, crt, key string) (*tls.Config, error) { | |
| caCertPEM, err := ioutil.ReadFile(ca) | |
| if err != nil { | |
| return nil, err | |
| } | |
| roots := x509.NewCertPool() | |
| ok := roots.AppendCertsFromPEM(caCertPEM) | |
| if !ok { | |
| panic("failed to parse root certificate") | |
| } | |
| cert, err := tls.LoadX509KeyPair(crt, key) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &tls.Config{ | |
| Certificates: []tls.Certificate{cert}, | |
| ClientAuth: tls.RequireAndVerifyClientCert, | |
| ClientCAs: roots, | |
| }, nil | |
| } | |
| func printConnState(conn *tls.Conn) { | |
| log.Print(">>>>>>>>>>>>>>>> State <<<<<<<<<<<<<<<<") | |
| state := conn.ConnectionState() | |
| log.Printf("Version: %x", state.Version) | |
| log.Printf("HandshakeComplete: %t", state.HandshakeComplete) | |
| log.Printf("DidResume: %t", state.DidResume) | |
| log.Printf("CipherSuite: %x", state.CipherSuite) | |
| log.Printf("NegotiatedProtocol: %s", state.NegotiatedProtocol) | |
| log.Printf("NegotiatedProtocolIsMutual: %t", state.NegotiatedProtocolIsMutual) | |
| log.Print("Certificate chain:") | |
| for i, cert := range state.PeerCertificates { | |
| subject := cert.Subject | |
| issuer := cert.Issuer | |
| log.Printf(" %d s:/C=%v/ST=%v/L=%v/O=%v/OU=%v/CN=%s", i, subject.Country, subject.Province, subject.Locality, subject.Organization, subject.OrganizationalUnit, subject.CommonName) | |
| log.Printf(" i:/C=%v/ST=%v/L=%v/O=%v/OU=%v/CN=%s", issuer.Country, issuer.Province, issuer.Locality, issuer.Organization, issuer.OrganizationalUnit, issuer.CommonName) | |
| } | |
| log.Print(">>>>>>>>>>>>>>>> State End <<<<<<<<<<<<<<<<") | |
| } | |
| func main() { | |
| listen := flag.String("listen", "localhost:4433", "which port to listen") | |
| ca := flag.String("ca", "./ca.crt", "root certificate") | |
| crt := flag.String("crt", "./server.crt", "certificate") | |
| key := flag.String("key", "./server.key", "key") | |
| flag.Parse() | |
| config, err := createServerConfig(*ca, *crt, *key) | |
| if err != nil { | |
| log.Fatal("config failed: %s", err.Error()) | |
| } | |
| ln, err := tls.Listen("tcp", *listen, config) | |
| if err != nil { | |
| log.Fatal("listen failed: %s", err.Error()) | |
| } | |
| log.Printf("listen on %s", *listen) | |
| for { | |
| conn, err := ln.Accept() | |
| if err != nil { | |
| log.Fatal("accept failed: %s", err.Error()) | |
| break | |
| } | |
| log.Printf("connection open: %s", conn.RemoteAddr()) | |
| printConnState(conn.(*tls.Conn)) | |
| go func(c net.Conn) { | |
| wr, _ := io.Copy(c, c) | |
| c.Close() | |
| log.Printf("connection close: %s, written: %d", conn.RemoteAddr(), wr) | |
| }(conn) | |
| } | |
| } |
Super useful example!
And for the last commenters; rather than sleeping you could ensure the tls handshake finishes before printing the connection state by calling conn.Handshake().
When I ran the same using go v1.15.
2021/02/02 17:49:15 failed to connect: x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0
In order to avoid this the server.crt needs to be generated using a different command as below.
openssl x509 -req -extfile <(printf "subjectAltName=DNS:localhost,DNS:www.example.com") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
Very good document!
Many thanks
Very simple example. Thank you.
A question, is there a problem if I dont pass rootCAs from the client side?
@Lonenso It is good to me by sleeping some time before printing on both the client and server side.
go func() {
time.Sleep(time.Nanosecond * 1)
printConnState(conn.(*tls.Conn))
}()