-
-
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))
}()