-
-
Save snail007/0e5b3d72c08230fe61fa03262595dd45 to your computer and use it in GitHub Desktop.
golang tls client and server, require and verify certificate in double direction,golang tls 双向验证
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}, | |
| ServerName: "server", | |
| RootCAs: roots, | |
| InsecureSkipVerify: false, | |
| }, 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() | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # >>>>>>>>>>>>>>>>>> 根证书 <<<<<<<<<<<<<<<<<<<<<< | |
| # 生成根证书私钥: 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=server | |
| # 签名服务器证书: 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=client | |
| # 签名客户端证书: client.crt | |
| openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 验证逻辑: | |
| 1.服务端会使用ClientCAs这里配置的证书里面公钥解密客户端的证书签名,然后验证客户端证书是否有效. | |
| 注意的地方就是服务端配置的ServerName就是生成客户端证书的时候客户端的CN=client这里对应. | |
| 如果服务端不配置ServerName,那么服务端验证的时候就不会比较ServerName,只是解密看看客户端证书是否有效. | |
| 总结: | |
| a.服务端如果没有配置ServerName,那么客户端证书只要是ClientCAs签发的即可,或者客户端的证书直接加入ClientCAs中,都是可以的. | |
| b.服务端配置了ServerName,那么客户端证书不仅要要是ClientCAs签发的或者客户端的证书直接加入ClientCAs中, | |
| 而且客户端证书生成的时候CN=xxx,xxx必须和服务端配置的ServerName一样. | |
| 2.客户端会使用RootCAs这里配置的证书里面公钥解密服务端的证书签名,然后验证客户端证书是否有效. | |
| 如果要求客户端验证服务端的证书InsecureSkipVerify必须是false,客户端必须配置ServerName, | |
| ServerName就是生成服务端证书的时候服务端的CN=server这里对应. | |
| 总结: | |
| 服务端证书必须是客户端配置的RootCAs签发的或者服务端证书在RootCAs中,客户端会用RootCAs解密服务端的证书, | |
| 并比较解密的"server name"是否和服务端证书里面的一致,来确定服务端证书的有效性. | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, | |
| ServerName: "client", | |
| 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) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment