mirror of https://github.com/XTLS/Xray-core
Refactor
parent
10a185bb65
commit
ef1d468f73
|
@ -406,7 +406,6 @@ type TLSConfig struct {
|
||||||
CipherSuites string `json:"cipherSuites"`
|
CipherSuites string `json:"cipherSuites"`
|
||||||
Fingerprint string `json:"fingerprint"`
|
Fingerprint string `json:"fingerprint"`
|
||||||
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
RejectUnknownSNI bool `json:"rejectUnknownSni"`
|
||||||
PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"`
|
|
||||||
PinnedPeerCertificateSha256 *[]string `json:"pinnedPeerCertificateSha256"`
|
PinnedPeerCertificateSha256 *[]string `json:"pinnedPeerCertificateSha256"`
|
||||||
CurvePreferences *StringList `json:"curvePreferences"`
|
CurvePreferences *StringList `json:"curvePreferences"`
|
||||||
MasterKeyLog string `json:"masterKeyLog"`
|
MasterKeyLog string `json:"masterKeyLog"`
|
||||||
|
@ -458,17 +457,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||||
}
|
}
|
||||||
config.RejectUnknownSni = c.RejectUnknownSNI
|
config.RejectUnknownSni = c.RejectUnknownSNI
|
||||||
|
|
||||||
if c.PinnedPeerCertificateChainSha256 != nil {
|
|
||||||
config.PinnedPeerCertificateChainSha256 = [][]byte{}
|
|
||||||
for _, v := range *c.PinnedPeerCertificateChainSha256 {
|
|
||||||
hashValue, err := base64.StdEncoding.DecodeString(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config.PinnedPeerCertificateChainSha256 = append(config.PinnedPeerCertificateChainSha256, hashValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.PinnedPeerCertificateSha256 != nil {
|
if c.PinnedPeerCertificateSha256 != nil {
|
||||||
config.PinnedPeerCertificateSha256 = [][]byte{}
|
config.PinnedPeerCertificateSha256 = [][]byte{}
|
||||||
for _, v := range *c.PinnedPeerCertificateSha256 {
|
for _, v := range *c.PinnedPeerCertificateSha256 {
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/main/commands/base"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmdCertChainHash = &base.Command{
|
|
||||||
UsageLine: "{{.Exec}} certChainHash",
|
|
||||||
Short: "Calculate TLS certificates hash.",
|
|
||||||
Long: `
|
|
||||||
xray tls certChainHash --cert <cert.pem>
|
|
||||||
Calculate TLS certificate chain hash.
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
cmdCertChainHash.Run = executeCertChainHash // break init loop
|
|
||||||
}
|
|
||||||
|
|
||||||
var input = cmdCertChainHash.Flag.String("cert", "fullchain.pem", "The file path of the certificates chain")
|
|
||||||
|
|
||||||
func executeCertChainHash(cmd *base.Command, args []string) {
|
|
||||||
fs := flag.NewFlagSet("certChainHash", flag.ContinueOnError)
|
|
||||||
if err := fs.Parse(args); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
certContent, err := os.ReadFile(*input)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
certChainHashB64 := tls.CalculatePEMCertChainSHA256Hash(certContent)
|
|
||||||
fmt.Println(certChainHashB64)
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdLeafCertHash = &base.Command{
|
||||||
|
UsageLine: "{{.Exec}} tls leafCertHash",
|
||||||
|
Short: "Calculate TLS leaf certificate hash.",
|
||||||
|
Long: `
|
||||||
|
xray tls leafCertHash --cert <cert.pem>
|
||||||
|
Calculate TLS leaf certificate hash.
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdLeafCertHash.Run = executeLeafCertHash // break init loop
|
||||||
|
}
|
||||||
|
|
||||||
|
var input = cmdLeafCertHash.Flag.String("cert", "fullchain.pem", "The file path of the leaf certificate")
|
||||||
|
|
||||||
|
func executeLeafCertHash(cmd *base.Command, args []string) {
|
||||||
|
fs := flag.NewFlagSet("leafCertHash", flag.ContinueOnError)
|
||||||
|
if err := fs.Parse(args); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certContent, err := os.ReadFile(*input)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certChainHashB64, err := tls.CalculatePEMLeafCertSHA256Hash(certContent)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to decode cert", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(certChainHashB64)
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package tls
|
||||||
import (
|
import (
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -156,8 +156,14 @@ func printTLSConnDetail(tlsConn *gotls.Conn) {
|
||||||
|
|
||||||
func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
hash := GenerateCertChainHash(rawCerts)
|
var hash []byte
|
||||||
fmt.Println("Certificate Chain Hash: ", base64.StdEncoding.EncodeToString(hash))
|
for _, asn1Data := range rawCerts {
|
||||||
|
cert, _ := x509.ParseCertificate(asn1Data)
|
||||||
|
if cert.IsCA {
|
||||||
|
hash = GenerateCertHash(cert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Certificate Leaf Hash: ", hex.EncodeToString(hash))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ var CmdTLS = &base.Command{
|
||||||
Commands: []*base.Command{
|
Commands: []*base.Command{
|
||||||
cmdCert,
|
cmdCert,
|
||||||
cmdPing,
|
cmdPing,
|
||||||
cmdCertChainHash,
|
cmdLeafCertHash,
|
||||||
cmdECH,
|
cmdECH,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ func TestSimpleTLSConnection(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -203,7 +203,7 @@ func TestAutoIssuingCertificate(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -304,7 +304,7 @@ func TestTLSOverKCP(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -400,7 +400,7 @@ func TestTLSOverWebSocket(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -512,7 +512,7 @@ func TestGRPC(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -624,7 +624,7 @@ func TestGRPCMultiMode(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -674,7 +674,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||||
defer tcpServer.Close()
|
defer tcpServer.Close()
|
||||||
certificateDer := cert.MustGenerate(nil)
|
certificateDer := cert.MustGenerate(nil)
|
||||||
certificate := tls.ParseCertificate(certificateDer)
|
certificate := tls.ParseCertificate(certificateDer)
|
||||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||||
userID := protocol.NewID(uuid.New())
|
userID := protocol.NewID(uuid.New())
|
||||||
serverPort := tcp.PickPort()
|
serverPort := tcp.PickPort()
|
||||||
serverConfig := &core.Config{
|
serverConfig := &core.Config{
|
||||||
|
@ -731,7 +731,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -743,8 +743,8 @@ func TestSimpleTLSConnectionPinned(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
AllowInsecure: true,
|
AllowInsecure: true,
|
||||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
PinnedPeerCertificateSha256: [][]byte{certHash},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -771,7 +771,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||||
defer tcpServer.Close()
|
defer tcpServer.Close()
|
||||||
certificateDer := cert.MustGenerate(nil)
|
certificateDer := cert.MustGenerate(nil)
|
||||||
certificate := tls.ParseCertificate(certificateDer)
|
certificate := tls.ParseCertificate(certificateDer)
|
||||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||||
certHash[1] += 1
|
certHash[1] += 1
|
||||||
userID := protocol.NewID(uuid.New())
|
userID := protocol.NewID(uuid.New())
|
||||||
serverPort := tcp.PickPort()
|
serverPort := tcp.PickPort()
|
||||||
|
@ -829,7 +829,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -841,8 +841,8 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
AllowInsecure: true,
|
AllowInsecure: true,
|
||||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
PinnedPeerCertificateSha256: [][]byte{certHash},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -869,7 +869,7 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||||
defer tcpServer.Close()
|
defer tcpServer.Close()
|
||||||
certificateDer := cert.MustGenerate(nil)
|
certificateDer := cert.MustGenerate(nil)
|
||||||
certificate := tls.ParseCertificate(certificateDer)
|
certificate := tls.ParseCertificate(certificateDer)
|
||||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||||
userID := protocol.NewID(uuid.New())
|
userID := protocol.NewID(uuid.New())
|
||||||
serverPort := tcp.PickPort()
|
serverPort := tcp.PickPort()
|
||||||
serverConfig := &core.Config{
|
serverConfig := &core.Config{
|
||||||
|
@ -926,7 +926,7 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -938,9 +938,9 @@ func TestUTLSConnectionPinned(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
Fingerprint: "random",
|
Fingerprint: "random",
|
||||||
AllowInsecure: true,
|
AllowInsecure: true,
|
||||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
PinnedPeerCertificateSha256: [][]byte{certHash},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -967,7 +967,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||||
defer tcpServer.Close()
|
defer tcpServer.Close()
|
||||||
certificateDer := cert.MustGenerate(nil)
|
certificateDer := cert.MustGenerate(nil)
|
||||||
certificate := tls.ParseCertificate(certificateDer)
|
certificate := tls.ParseCertificate(certificateDer)
|
||||||
certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate})
|
certHash := tls.GenerateCertHash(certificateDer.Certificate)
|
||||||
certHash[1] += 1
|
certHash[1] += 1
|
||||||
userID := protocol.NewID(uuid.New())
|
userID := protocol.NewID(uuid.New())
|
||||||
serverPort := tcp.PickPort()
|
serverPort := tcp.PickPort()
|
||||||
|
@ -1025,7 +1025,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||||
Receiver: &protocol.ServerEndpoint{
|
Receiver: &protocol.ServerEndpoint{
|
||||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||||
Port: uint32(serverPort),
|
Port: uint32(serverPort),
|
||||||
User: &protocol.User{
|
User: &protocol.User{
|
||||||
Account: serial.ToTypedMessage(&vmess.Account{
|
Account: serial.ToTypedMessage(&vmess.Account{
|
||||||
Id: userID.String(),
|
Id: userID.String(),
|
||||||
}),
|
}),
|
||||||
|
@ -1037,9 +1037,9 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) {
|
||||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||||
SecuritySettings: []*serial.TypedMessage{
|
SecuritySettings: []*serial.TypedMessage{
|
||||||
serial.ToTypedMessage(&tls.Config{
|
serial.ToTypedMessage(&tls.Config{
|
||||||
Fingerprint: "random",
|
Fingerprint: "random",
|
||||||
AllowInsecure: true,
|
AllowInsecure: true,
|
||||||
PinnedPeerCertificateChainSha256: [][]byte{certHash},
|
PinnedPeerCertificateSha256: [][]byte{certHash},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -281,31 +280,25 @@ func (c *Config) parseServerName() string {
|
||||||
return c.ServerName
|
return c.ServerName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) (err error) {
|
||||||
// pinned chain has the highest priority
|
// extract x509 certificates from rawCerts(verifiedChains will be nil if InsecureSkipVerify is true)
|
||||||
// pass if successfull and fail if not
|
certs := make([]*x509.Certificate, len(rawCerts))
|
||||||
if r.PinnedPeerCertificateChainSha256 != nil {
|
for i, asn1Data := range rawCerts {
|
||||||
hashValue := GenerateCertChainHash(rawCerts)
|
certs[i], _ = x509.ParseCertificate(asn1Data)
|
||||||
for _, v := range r.PinnedPeerCertificateChainSha256 {
|
|
||||||
if hmac.Equal(hashValue, v) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// directly return success if pinned cert is leaf
|
// directly return success if pinned cert is leaf
|
||||||
// or add the CA to RootCAs if pinned cert is CA(and can be used in VerifyPeerCertInNames for Self signed CA)
|
// or add the CA to RootCAs if pinned cert is CA(and can be used in VerifyPeerCertInNames for Self signed CA)
|
||||||
RootCAs := r.RootCAs
|
RootCAs := r.RootCAs
|
||||||
if r.PinnedPeerCertificateSha256 != nil {
|
if r.PinnedPeerCertificateSha256 != nil {
|
||||||
verifyResult, verifiedCert := verifyChain(verifiedChains, r.PinnedPeerCertificateSha256)
|
verifyResult, verifiedCert := verifyChain(certs, r.PinnedPeerCertificateSha256)
|
||||||
switch verifyResult {
|
switch verifyResult {
|
||||||
case certNotFound:
|
case certNotFound:
|
||||||
return errors.New("peer cert is unrecognized")
|
return errors.New("peer cert is unrecognized")
|
||||||
case foundLeaf:
|
case foundLeaf:
|
||||||
return nil
|
return nil
|
||||||
case foundCA:
|
case foundCA:
|
||||||
RootCAs = RootCAs.Clone()
|
RootCAs = x509.NewCertPool()
|
||||||
RootCAs.AddCert(verifiedCert)
|
RootCAs.AddCert(verifiedCert)
|
||||||
default:
|
default:
|
||||||
panic("impossible PinnedPeerCertificateSha256 verify result")
|
panic("impossible PinnedPeerCertificateSha256 verify result")
|
||||||
|
@ -314,10 +307,6 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||||
|
|
||||||
if r.VerifyPeerCertInNames != nil {
|
if r.VerifyPeerCertInNames != nil {
|
||||||
if len(r.VerifyPeerCertInNames) > 0 {
|
if len(r.VerifyPeerCertInNames) > 0 {
|
||||||
certs := make([]*x509.Certificate, len(rawCerts))
|
|
||||||
for i, asn1Data := range rawCerts {
|
|
||||||
certs[i], _ = x509.ParseCertificate(asn1Data)
|
|
||||||
}
|
|
||||||
opts := x509.VerifyOptions{
|
opts := x509.VerifyOptions{
|
||||||
Roots: RootCAs,
|
Roots: RootCAs,
|
||||||
CurrentTime: time.Now(),
|
CurrentTime: time.Now(),
|
||||||
|
@ -337,10 +326,9 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
||||||
}
|
}
|
||||||
|
|
||||||
type RandCarrier struct {
|
type RandCarrier struct {
|
||||||
RootCAs *x509.CertPool
|
RootCAs *x509.CertPool
|
||||||
VerifyPeerCertInNames []string
|
VerifyPeerCertInNames []string
|
||||||
PinnedPeerCertificateChainSha256 [][]byte
|
PinnedPeerCertificateSha256 [][]byte
|
||||||
PinnedPeerCertificateSha256 [][]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
func (r *RandCarrier) Read(p []byte) (n int, err error) {
|
||||||
|
@ -365,10 +353,9 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
randCarrier := &RandCarrier{
|
randCarrier := &RandCarrier{
|
||||||
RootCAs: root,
|
RootCAs: root,
|
||||||
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames),
|
||||||
PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256,
|
PinnedPeerCertificateSha256: c.PinnedPeerCertificateSha256,
|
||||||
PinnedPeerCertificateSha256: c.PinnedPeerCertificateSha256,
|
|
||||||
}
|
}
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
Rand: randCarrier,
|
Rand: randCarrier,
|
||||||
|
@ -538,19 +525,17 @@ const (
|
||||||
foundCA
|
foundCA
|
||||||
)
|
)
|
||||||
|
|
||||||
func verifyChain(verifiedChains [][]*x509.Certificate, PinnedPeerCertificateSha256 [][]byte) (verifyResult, *x509.Certificate) {
|
func verifyChain(certs []*x509.Certificate, PinnedPeerCertificateSha256 [][]byte) (verifyResult, *x509.Certificate) {
|
||||||
for _, v := range verifiedChains {
|
for _, cert := range certs {
|
||||||
for _, cert := range v {
|
certHash := GenerateCertHash(cert)
|
||||||
certHash := GenerateCertHash(cert)
|
for _, c := range PinnedPeerCertificateSha256 {
|
||||||
for _, c := range PinnedPeerCertificateSha256 {
|
if hmac.Equal(certHash, c) {
|
||||||
if hmac.Equal(certHash, c) {
|
if cert.IsCA {
|
||||||
if cert.IsCA {
|
return foundCA, cert
|
||||||
return foundCA, cert
|
} else {
|
||||||
} else {
|
return foundLeaf, cert
|
||||||
return foundLeaf, cert
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,40 +3,37 @@ package tls
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CalculatePEMCertChainSHA256Hash(certContent []byte) string {
|
func CalculatePEMLeafCertSHA256Hash(certContent []byte) (string, error) {
|
||||||
var certChain [][]byte
|
var leafCert *x509.Certificate
|
||||||
for {
|
for {
|
||||||
|
var err error
|
||||||
block, remain := pem.Decode(certContent)
|
block, remain := pem.Decode(certContent)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
certChain = append(certChain, block.Bytes)
|
leafCert, err = x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
certContent = remain
|
certContent = remain
|
||||||
}
|
}
|
||||||
certChainHash := GenerateCertChainHash(certChain)
|
certHash := GenerateCertHash(leafCert)
|
||||||
certChainHashB64 := base64.StdEncoding.EncodeToString(certChainHash)
|
certHashHex := hex.EncodeToString(certHash)
|
||||||
return certChainHashB64
|
return certHashHex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateCertChainHash(rawCerts [][]byte) []byte {
|
// []byte must be ASN.1 DER content
|
||||||
var hashValue []byte
|
func GenerateCertHash[T *x509.Certificate | []byte](cert T) []byte {
|
||||||
for _, certValue := range rawCerts {
|
var out [32]byte
|
||||||
out := sha256.Sum256(certValue)
|
switch v := any(cert).(type) {
|
||||||
if hashValue == nil {
|
case *x509.Certificate:
|
||||||
hashValue = out[:]
|
out = sha256.Sum256(v.Raw)
|
||||||
} else {
|
case []byte:
|
||||||
newHashValue := sha256.Sum256(append(hashValue, out[:]...))
|
out = sha256.Sum256(v)
|
||||||
hashValue = newHashValue[:]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return hashValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateCertHash(cert *x509.Certificate) []byte {
|
|
||||||
out := sha256.Sum256(cert.Raw)
|
|
||||||
return out[:]
|
return out[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,109 +9,6 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCalculateCertChainHash(t *testing.T) {
|
|
||||||
/* This is used to make sure that the hash signature generated is consistent
|
|
||||||
Do NOT change this test to suit your modification.
|
|
||||||
*/
|
|
||||||
const CertBundle = `
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFJjCCBA6gAwIBAgISBL8FgUdEcVYEjdMkTZPgC3ocMA0GCSqGSIb3DQEBCwUA
|
|
||||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
|
|
||||||
EwJSMzAeFw0yMTAzMjkwMTM2MzlaFw0yMTA2MjcwMTM2MzlaMBsxGTAXBgNVBAMT
|
|
||||||
EHNlY3VyZS5ra2Rldi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
|
||||||
AQDOF/j+s7rHaDMXdhYjffoOFjNZb7n3sCuvubI3qOcgJmr1WPlCEry50KoY8FaB
|
|
||||||
IF2HstMIZceN4NoUK7mr3WAvsQTA47uBfuhp+XQmAQW0T/fYD+XbvxtCENEin+xm
|
|
||||||
JsvTKZLTKbE08E964J4H+1sBmueP6rvy2Wt95z0XkqoQiikpmLE87WdltQcATvVX
|
|
||||||
qqrL64hV0nN4Hdi2Bv1cQ92aR7lZGj8jiQRtTj8y5Ah3Gk3fPoao+yI7gnzembqo
|
|
||||||
fddePzz/u8iEuvYAsIYZKn9bbS7rkYoJazL2/xiDZR7usn0SomzmM6lGXDD3FF4b
|
|
||||||
lyTkLYwgFVgbGWoz1+eOHD5BAgMBAAGjggJLMIICRzAOBgNVHQ8BAf8EBAMCBaAw
|
|
||||||
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD
|
|
||||||
VR0OBBYEFOPRdApL8XENLXDuU3oPisykGyp+MB8GA1UdIwQYMBaAFBQusxe3WFbL
|
|
||||||
rlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDov
|
|
||||||
L3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5v
|
|
||||||
cmcvMBsGA1UdEQQUMBKCEHNlY3VyZS5ra2Rldi5vcmcwTAYDVR0gBEUwQzAIBgZn
|
|
||||||
gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
|
|
||||||
ZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQD2XJQv0Xcw
|
|
||||||
IhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXh71yBGAAAEAwBGMEQCIDmziDOn
|
|
||||||
ehPY2KoAFX8fPWiCm4EBTbGJXBWF1LCotPJBAiBLSCg+krXvbyoABnTm8knv0hbG
|
|
||||||
/ZOk8LV6qpw9VoQwGwB3AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kT
|
|
||||||
AAABeHvXIIAAAAQDAEgwRgIhAOkeKc52wR3n5QWZfa3zbbicMMSQrTGbQ+1fHNs7
|
|
||||||
SsRvAiEAqbflDx1nZRsTA22FfNYfgF6v5Z3/svjiTleWSQad4WswDQYJKoZIhvcN
|
|
||||||
AQELBQADggEBAEj8tg+Agf5sNBM9CvjeXbA0fkpGDaQzXEkwefAea5vPgKoGiWSN
|
|
||||||
pHDkyr0i7+mqa7cMXhmmo20V0/+QDv0nrsCw8pgJuviC3GvK6agT6WfkXM2djJuo
|
|
||||||
osPeXOL9KEF/ATv0EyM5tr9TIoRSSYQoRhuqEdg3Dz9Ii8GXR5HhbYr6Cd7gwNHS
|
|
||||||
kYeivKDmgv31GHb4twPSE9LZ/U+56lNVvSbJ4csupIF3GnxnxrFSmijYNOPoM6mj
|
|
||||||
tzY45d4mjPs0fKCFKSsVM6YT0tX4NwIKsOaeQg30WLtRyDwYm6ma/a/UUUS0FloZ
|
|
||||||
2/p85glOgzownfoRjzTbqHu8ewtMd6Apc0E=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
|
|
||||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
|
||||||
DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
|
|
||||||
MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
|
|
||||||
AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
|
|
||||||
jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
|
|
||||||
Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
|
|
||||||
U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
|
|
||||||
gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
|
|
||||||
/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
|
|
||||||
oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
|
|
||||||
BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
|
|
||||||
ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
|
|
||||||
p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
|
|
||||||
AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
|
|
||||||
Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
|
|
||||||
LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
|
|
||||||
r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
|
|
||||||
AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
|
|
||||||
ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
|
|
||||||
S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
|
|
||||||
qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
|
|
||||||
O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
|
|
||||||
UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
`
|
|
||||||
t.Run("bundle", func(t *testing.T) {
|
|
||||||
hash := CalculatePEMCertChainSHA256Hash([]byte(CertBundle))
|
|
||||||
assert.Equal(t, "WF65fBkgltadMnXryOMZ6TEYeV4d5Q0uu4SGXGZ0RjI=", hash)
|
|
||||||
})
|
|
||||||
const Single = `-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFJjCCBA6gAwIBAgISBL8FgUdEcVYEjdMkTZPgC3ocMA0GCSqGSIb3DQEBCwUA
|
|
||||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
|
|
||||||
EwJSMzAeFw0yMTAzMjkwMTM2MzlaFw0yMTA2MjcwMTM2MzlaMBsxGTAXBgNVBAMT
|
|
||||||
EHNlY3VyZS5ra2Rldi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
|
||||||
AQDOF/j+s7rHaDMXdhYjffoOFjNZb7n3sCuvubI3qOcgJmr1WPlCEry50KoY8FaB
|
|
||||||
IF2HstMIZceN4NoUK7mr3WAvsQTA47uBfuhp+XQmAQW0T/fYD+XbvxtCENEin+xm
|
|
||||||
JsvTKZLTKbE08E964J4H+1sBmueP6rvy2Wt95z0XkqoQiikpmLE87WdltQcATvVX
|
|
||||||
qqrL64hV0nN4Hdi2Bv1cQ92aR7lZGj8jiQRtTj8y5Ah3Gk3fPoao+yI7gnzembqo
|
|
||||||
fddePzz/u8iEuvYAsIYZKn9bbS7rkYoJazL2/xiDZR7usn0SomzmM6lGXDD3FF4b
|
|
||||||
lyTkLYwgFVgbGWoz1+eOHD5BAgMBAAGjggJLMIICRzAOBgNVHQ8BAf8EBAMCBaAw
|
|
||||||
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD
|
|
||||||
VR0OBBYEFOPRdApL8XENLXDuU3oPisykGyp+MB8GA1UdIwQYMBaAFBQusxe3WFbL
|
|
||||||
rlAJQOYfr52LFMLGMFUGCCsGAQUFBwEBBEkwRzAhBggrBgEFBQcwAYYVaHR0cDov
|
|
||||||
L3IzLm8ubGVuY3Iub3JnMCIGCCsGAQUFBzAChhZodHRwOi8vcjMuaS5sZW5jci5v
|
|
||||||
cmcvMBsGA1UdEQQUMBKCEHNlY3VyZS5ra2Rldi5vcmcwTAYDVR0gBEUwQzAIBgZn
|
|
||||||
gQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5s
|
|
||||||
ZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1BIHyAPAAdQD2XJQv0Xcw
|
|
||||||
IhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXh71yBGAAAEAwBGMEQCIDmziDOn
|
|
||||||
ehPY2KoAFX8fPWiCm4EBTbGJXBWF1LCotPJBAiBLSCg+krXvbyoABnTm8knv0hbG
|
|
||||||
/ZOk8LV6qpw9VoQwGwB3AG9Tdqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kT
|
|
||||||
AAABeHvXIIAAAAQDAEgwRgIhAOkeKc52wR3n5QWZfa3zbbicMMSQrTGbQ+1fHNs7
|
|
||||||
SsRvAiEAqbflDx1nZRsTA22FfNYfgF6v5Z3/svjiTleWSQad4WswDQYJKoZIhvcN
|
|
||||||
AQELBQADggEBAEj8tg+Agf5sNBM9CvjeXbA0fkpGDaQzXEkwefAea5vPgKoGiWSN
|
|
||||||
pHDkyr0i7+mqa7cMXhmmo20V0/+QDv0nrsCw8pgJuviC3GvK6agT6WfkXM2djJuo
|
|
||||||
osPeXOL9KEF/ATv0EyM5tr9TIoRSSYQoRhuqEdg3Dz9Ii8GXR5HhbYr6Cd7gwNHS
|
|
||||||
kYeivKDmgv31GHb4twPSE9LZ/U+56lNVvSbJ4csupIF3GnxnxrFSmijYNOPoM6mj
|
|
||||||
tzY45d4mjPs0fKCFKSsVM6YT0tX4NwIKsOaeQg30WLtRyDwYm6ma/a/UUUS0FloZ
|
|
||||||
2/p85glOgzownfoRjzTbqHu8ewtMd6Apc0E=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
`
|
|
||||||
t.Run("single", func(t *testing.T) {
|
|
||||||
hash := CalculatePEMCertChainSHA256Hash([]byte(Single))
|
|
||||||
assert.Equal(t, "FW3SVMCL6um2wVltOdgJ3DpI82aredw83YoCblkMkVM=", hash)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCalculateCertHash(t *testing.T) {
|
func TestCalculateCertHash(t *testing.T) {
|
||||||
const Single = `-----BEGIN CERTIFICATE-----
|
const Single = `-----BEGIN CERTIFICATE-----
|
||||||
MIINWzCCC0OgAwIBAgITMwK6ajqdrV0tahuIrQAAArpqOjANBgkqhkiG9w0BAQwF
|
MIINWzCCC0OgAwIBAgITMwK6ajqdrV0tahuIrQAAArpqOjANBgkqhkiG9w0BAQwF
|
||||||
|
|
Loading…
Reference in New Issue