From ef1d468f730660eb3073283aea82bca037cdc6d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Tue, 16 Sep 2025 16:40:05 +0000 Subject: [PATCH] Refactor --- infra/conf/transport_internet.go | 12 --- main/commands/all/tls/certchainhash.go | 40 ---------- main/commands/all/tls/leafcerthash.go | 44 +++++++++++ main/commands/all/tls/ping.go | 12 ++- main/commands/all/tls/tls.go | 2 +- testing/scenarios/tls_test.go | 48 ++++++------ transport/internet/tls/config.go | 61 ++++++--------- transport/internet/tls/pin.go | 41 +++++----- transport/internet/tls/pin_test.go | 103 ------------------------- 9 files changed, 120 insertions(+), 243 deletions(-) delete mode 100644 main/commands/all/tls/certchainhash.go create mode 100644 main/commands/all/tls/leafcerthash.go diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 7ac3eb51..ea4ed6ff 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -406,7 +406,6 @@ type TLSConfig struct { CipherSuites string `json:"cipherSuites"` Fingerprint string `json:"fingerprint"` RejectUnknownSNI bool `json:"rejectUnknownSni"` - PinnedPeerCertificateChainSha256 *[]string `json:"pinnedPeerCertificateChainSha256"` PinnedPeerCertificateSha256 *[]string `json:"pinnedPeerCertificateSha256"` CurvePreferences *StringList `json:"curvePreferences"` MasterKeyLog string `json:"masterKeyLog"` @@ -458,17 +457,6 @@ func (c *TLSConfig) Build() (proto.Message, error) { } 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 { config.PinnedPeerCertificateSha256 = [][]byte{} for _, v := range *c.PinnedPeerCertificateSha256 { diff --git a/main/commands/all/tls/certchainhash.go b/main/commands/all/tls/certchainhash.go deleted file mode 100644 index 304f668d..00000000 --- a/main/commands/all/tls/certchainhash.go +++ /dev/null @@ -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 - 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) -} diff --git a/main/commands/all/tls/leafcerthash.go b/main/commands/all/tls/leafcerthash.go new file mode 100644 index 00000000..06bbf5cd --- /dev/null +++ b/main/commands/all/tls/leafcerthash.go @@ -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 + 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) +} diff --git a/main/commands/all/tls/ping.go b/main/commands/all/tls/ping.go index ccc5fe6b..6417b74c 100644 --- a/main/commands/all/tls/ping.go +++ b/main/commands/all/tls/ping.go @@ -3,7 +3,7 @@ package tls import ( gotls "crypto/tls" "crypto/x509" - "encoding/base64" + "encoding/hex" "fmt" "net" "strconv" @@ -156,8 +156,14 @@ func printTLSConnDetail(tlsConn *gotls.Conn) { func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - hash := GenerateCertChainHash(rawCerts) - fmt.Println("Certificate Chain Hash: ", base64.StdEncoding.EncodeToString(hash)) + var hash []byte + for _, asn1Data := range rawCerts { + cert, _ := x509.ParseCertificate(asn1Data) + if cert.IsCA { + hash = GenerateCertHash(cert) + } + } + fmt.Println("Certificate Leaf Hash: ", hex.EncodeToString(hash)) return nil } } diff --git a/main/commands/all/tls/tls.go b/main/commands/all/tls/tls.go index a93da1c3..159bf769 100644 --- a/main/commands/all/tls/tls.go +++ b/main/commands/all/tls/tls.go @@ -13,7 +13,7 @@ var CmdTLS = &base.Command{ Commands: []*base.Command{ cmdCert, cmdPing, - cmdCertChainHash, + cmdLeafCertHash, cmdECH, }, } diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index 250e5d47..fe58b33b 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -92,7 +92,7 @@ func TestSimpleTLSConnection(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -203,7 +203,7 @@ func TestAutoIssuingCertificate(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -304,7 +304,7 @@ func TestTLSOverKCP(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -400,7 +400,7 @@ func TestTLSOverWebSocket(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -512,7 +512,7 @@ func TestGRPC(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -624,7 +624,7 @@ func TestGRPCMultiMode(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -674,7 +674,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) { defer tcpServer.Close() certificateDer := cert.MustGenerate(nil) certificate := tls.ParseCertificate(certificateDer) - certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate}) + certHash := tls.GenerateCertHash(certificateDer.Certificate) userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ @@ -731,7 +731,7 @@ func TestSimpleTLSConnectionPinned(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -743,8 +743,8 @@ func TestSimpleTLSConnectionPinned(t *testing.T) { SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - PinnedPeerCertificateChainSha256: [][]byte{certHash}, + AllowInsecure: true, + PinnedPeerCertificateSha256: [][]byte{certHash}, }), }, }, @@ -771,7 +771,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) { defer tcpServer.Close() certificateDer := cert.MustGenerate(nil) certificate := tls.ParseCertificate(certificateDer) - certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate}) + certHash := tls.GenerateCertHash(certificateDer.Certificate) certHash[1] += 1 userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() @@ -829,7 +829,7 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -841,8 +841,8 @@ func TestSimpleTLSConnectionPinnedWrongCert(t *testing.T) { SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ - AllowInsecure: true, - PinnedPeerCertificateChainSha256: [][]byte{certHash}, + AllowInsecure: true, + PinnedPeerCertificateSha256: [][]byte{certHash}, }), }, }, @@ -869,7 +869,7 @@ func TestUTLSConnectionPinned(t *testing.T) { defer tcpServer.Close() certificateDer := cert.MustGenerate(nil) certificate := tls.ParseCertificate(certificateDer) - certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate}) + certHash := tls.GenerateCertHash(certificateDer.Certificate) userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() serverConfig := &core.Config{ @@ -926,7 +926,7 @@ func TestUTLSConnectionPinned(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -938,9 +938,9 @@ func TestUTLSConnectionPinned(t *testing.T) { SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ - Fingerprint: "random", - AllowInsecure: true, - PinnedPeerCertificateChainSha256: [][]byte{certHash}, + Fingerprint: "random", + AllowInsecure: true, + PinnedPeerCertificateSha256: [][]byte{certHash}, }), }, }, @@ -967,7 +967,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) { defer tcpServer.Close() certificateDer := cert.MustGenerate(nil) certificate := tls.ParseCertificate(certificateDer) - certHash := tls.GenerateCertChainHash([][]byte{certificateDer.Certificate}) + certHash := tls.GenerateCertHash(certificateDer.Certificate) certHash[1] += 1 userID := protocol.NewID(uuid.New()) serverPort := tcp.PickPort() @@ -1025,7 +1025,7 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) { Receiver: &protocol.ServerEndpoint{ Address: net.NewIPOrDomain(net.LocalHostIP), Port: uint32(serverPort), - User: &protocol.User{ + User: &protocol.User{ Account: serial.ToTypedMessage(&vmess.Account{ Id: userID.String(), }), @@ -1037,9 +1037,9 @@ func TestUTLSConnectionPinnedWrongCert(t *testing.T) { SecurityType: serial.GetMessageType(&tls.Config{}), SecuritySettings: []*serial.TypedMessage{ serial.ToTypedMessage(&tls.Config{ - Fingerprint: "random", - AllowInsecure: true, - PinnedPeerCertificateChainSha256: [][]byte{certHash}, + Fingerprint: "random", + AllowInsecure: true, + PinnedPeerCertificateSha256: [][]byte{certHash}, }), }, }, diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index ec3ccbc2..b9ba0cae 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -7,7 +7,6 @@ import ( "crypto/rand" "crypto/tls" "crypto/x509" - "encoding/base64" "os" "slices" "strings" @@ -281,31 +280,25 @@ func (c *Config) parseServerName() string { return c.ServerName } -func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - // pinned chain has the highest priority - // pass if successfull and fail if not - if r.PinnedPeerCertificateChainSha256 != nil { - hashValue := GenerateCertChainHash(rawCerts) - for _, v := range r.PinnedPeerCertificateChainSha256 { - if hmac.Equal(hashValue, v) { - return nil - } - } - return errors.New("peer cert is unrecognized: ", base64.StdEncoding.EncodeToString(hashValue)) +func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) (err error) { + // extract x509 certificates from rawCerts(verifiedChains will be nil if InsecureSkipVerify is true) + certs := make([]*x509.Certificate, len(rawCerts)) + for i, asn1Data := range rawCerts { + certs[i], _ = x509.ParseCertificate(asn1Data) } // 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) RootCAs := r.RootCAs if r.PinnedPeerCertificateSha256 != nil { - verifyResult, verifiedCert := verifyChain(verifiedChains, r.PinnedPeerCertificateSha256) + verifyResult, verifiedCert := verifyChain(certs, r.PinnedPeerCertificateSha256) switch verifyResult { case certNotFound: return errors.New("peer cert is unrecognized") case foundLeaf: return nil case foundCA: - RootCAs = RootCAs.Clone() + RootCAs = x509.NewCertPool() RootCAs.AddCert(verifiedCert) default: panic("impossible PinnedPeerCertificateSha256 verify result") @@ -314,10 +307,6 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509 if r.VerifyPeerCertInNames != nil { if len(r.VerifyPeerCertInNames) > 0 { - certs := make([]*x509.Certificate, len(rawCerts)) - for i, asn1Data := range rawCerts { - certs[i], _ = x509.ParseCertificate(asn1Data) - } opts := x509.VerifyOptions{ Roots: RootCAs, CurrentTime: time.Now(), @@ -337,10 +326,9 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509 } type RandCarrier struct { - RootCAs *x509.CertPool - VerifyPeerCertInNames []string - PinnedPeerCertificateChainSha256 [][]byte - PinnedPeerCertificateSha256 [][]byte + RootCAs *x509.CertPool + VerifyPeerCertInNames []string + PinnedPeerCertificateSha256 [][]byte } func (r *RandCarrier) Read(p []byte) (n int, err error) { @@ -365,10 +353,9 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { } randCarrier := &RandCarrier{ - RootCAs: root, - VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames), - PinnedPeerCertificateChainSha256: c.PinnedPeerCertificateChainSha256, - PinnedPeerCertificateSha256: c.PinnedPeerCertificateSha256, + RootCAs: root, + VerifyPeerCertInNames: slices.Clone(c.VerifyPeerCertInNames), + PinnedPeerCertificateSha256: c.PinnedPeerCertificateSha256, } config := &tls.Config{ Rand: randCarrier, @@ -538,19 +525,17 @@ const ( foundCA ) -func verifyChain(verifiedChains [][]*x509.Certificate, PinnedPeerCertificateSha256 [][]byte) (verifyResult, *x509.Certificate) { - for _, v := range verifiedChains { - for _, cert := range v { - certHash := GenerateCertHash(cert) - for _, c := range PinnedPeerCertificateSha256 { - if hmac.Equal(certHash, c) { - if cert.IsCA { - return foundCA, cert - } else { - return foundLeaf, cert - } - +func verifyChain(certs []*x509.Certificate, PinnedPeerCertificateSha256 [][]byte) (verifyResult, *x509.Certificate) { + for _, cert := range certs { + certHash := GenerateCertHash(cert) + for _, c := range PinnedPeerCertificateSha256 { + if hmac.Equal(certHash, c) { + if cert.IsCA { + return foundCA, cert + } else { + return foundLeaf, cert } + } } } diff --git a/transport/internet/tls/pin.go b/transport/internet/tls/pin.go index 2bbaf614..060f5d73 100644 --- a/transport/internet/tls/pin.go +++ b/transport/internet/tls/pin.go @@ -3,40 +3,37 @@ package tls import ( "crypto/sha256" "crypto/x509" - "encoding/base64" + "encoding/hex" "encoding/pem" ) -func CalculatePEMCertChainSHA256Hash(certContent []byte) string { - var certChain [][]byte +func CalculatePEMLeafCertSHA256Hash(certContent []byte) (string, error) { + var leafCert *x509.Certificate for { + var err error block, remain := pem.Decode(certContent) if block == nil { break } - certChain = append(certChain, block.Bytes) + leafCert, err = x509.ParseCertificate(block.Bytes) + if err != nil { + return "", err + } certContent = remain } - certChainHash := GenerateCertChainHash(certChain) - certChainHashB64 := base64.StdEncoding.EncodeToString(certChainHash) - return certChainHashB64 + certHash := GenerateCertHash(leafCert) + certHashHex := hex.EncodeToString(certHash) + return certHashHex, nil } -func GenerateCertChainHash(rawCerts [][]byte) []byte { - var hashValue []byte - for _, certValue := range rawCerts { - out := sha256.Sum256(certValue) - if hashValue == nil { - hashValue = out[:] - } else { - newHashValue := sha256.Sum256(append(hashValue, out[:]...)) - hashValue = newHashValue[:] - } +// []byte must be ASN.1 DER content +func GenerateCertHash[T *x509.Certificate | []byte](cert T) []byte { + var out [32]byte + switch v := any(cert).(type) { + case *x509.Certificate: + out = sha256.Sum256(v.Raw) + case []byte: + out = sha256.Sum256(v) } - return hashValue -} - -func GenerateCertHash(cert *x509.Certificate) []byte { - out := sha256.Sum256(cert.Raw) return out[:] } diff --git a/transport/internet/tls/pin_test.go b/transport/internet/tls/pin_test.go index 3b2091bf..e0558c45 100644 --- a/transport/internet/tls/pin_test.go +++ b/transport/internet/tls/pin_test.go @@ -9,109 +9,6 @@ import ( "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) { const Single = `-----BEGIN CERTIFICATE----- MIINWzCCC0OgAwIBAgITMwK6ajqdrV0tahuIrQAAArpqOjANBgkqhkiG9w0BAQwF