certificate pinning for servers without domain

This commit is contained in:
luyuhuang 2020-09-05 11:52:33 +08:00 committed by ginuerzh
parent f7995ab564
commit fd079dd066
2 changed files with 59 additions and 29 deletions

View File

@ -3,12 +3,14 @@ package main
import ( import (
"crypto/sha256" "crypto/sha256"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"os" "os"
"strings" "strings"
"time"
"github.com/ginuerzh/gost" "github.com/ginuerzh/gost"
"github.com/go-log/log" "github.com/go-log/log"
@ -128,6 +130,31 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
InsecureSkipVerify: !node.GetBool("secure"), InsecureSkipVerify: !node.GetBool("secure"),
RootCAs: rootCAs, RootCAs: rootCAs,
} }
// If the argument `ca` is given, but not open `secure`, we verify the
// certificate manually.
if rootCAs != nil && !node.GetBool("secure") {
tlsCfg.VerifyConnection = func(state tls.ConnectionState) error {
opts := x509.VerifyOptions{
Roots: rootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := state.PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
return err
}
}
if cert, err := tls.LoadX509KeyPair(node.Get("cert"), node.Get("key")); err == nil { if cert, err := tls.LoadX509KeyPair(node.Get("cert"), node.Get("key")); err == nil {
tlsCfg.Certificates = []tls.Certificate{cert} tlsCfg.Certificates = []tls.Certificate{cert}
} }

61
tls.go
View File

@ -2,7 +2,6 @@ package gost
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509"
"errors" "errors"
"net" "net"
"sync" "sync"
@ -290,36 +289,40 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration)
return nil, err return nil, err
} }
// If crypto/tls is doing verification, there's no need to do our own. // We can do this in `tls.Config.VerifyConnection`, which effective for
if tlsConfig.InsecureSkipVerify == false { // other TLS protocols such as WebSocket. See `route.go:parseChainNode`
return tlsConn, nil /*
} // If crypto/tls is doing verification, there's no need to do our own.
if tlsConfig.InsecureSkipVerify == false {
// Similarly if we use host's CA, we can do full handshake return tlsConn, nil
if tlsConfig.RootCAs == nil {
return tlsConn, nil
}
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
} }
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts) // Similarly if we use host's CA, we can do full handshake
if err != nil { if tlsConfig.RootCAs == nil {
tlsConn.Close() return tlsConn, nil
return nil, err }
}
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
tlsConn.Close()
return nil, err
}
*/
return tlsConn, err return tlsConn, err
} }