Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified cert.db
Binary file not shown.
146 changes: 146 additions & 0 deletions certdata/digicert_g5_cross_sign_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package main

import (
"bytes"
"crypto/sha256"
"crypto/x509"
"fmt"
"testing"

"github.com/cloudflare/cfssl/ubiquity"
)

const metadataFile = "../ca-bundle.crt.metadata"

// digiCertG5CrossSign describes the single cross-sign carried for a DigiCert
// G5 TLS root so compatible-mode bundling can anchor its chains at an older,
// more widely trusted DigiCert root. No keystore in the metadata trusts the
// G5 roots themselves.
type digiCertG5CrossSign struct {
g5Root string
olderRoot string
fingerprint string
}

// Both cross-signs are valid 2026-03-31 to 2036-03-30, assert anyPolicy, and
// use the digicert.com AIA/CRL endpoints. Other cross-signs of these roots
// are revoked (2022/2023 issuances), restrict certificate policies (2026-06-10
// issuances), or point at digicert.cn endpoints.
var digiCertG5CrossSigns = []digiCertG5CrossSign{
{
// Serial 07A1278A2127F2B4C42170ECEC734B4E.
g5Root: "DigiCert TLS RSA4096 Root G5",
olderRoot: "DigiCert Global Root G2",
fingerprint: "60c2666da5e2332170e1d3bfcd95bf8c9a6d4e63473f3a8bbb8cf42b3f9f038a",
},
{
// Serial 0DF2A81F6BBE9A48DACFF728F67AB1FC.
g5Root: "DigiCert TLS ECC P384 Root G5",
olderRoot: "DigiCert Global Root G3",
fingerprint: "3142f99d358d9b500e5cdd2dff8f719a8ccecf589652334848522cca807d0807",
},
}

func findSelfSignedRoot(t *testing.T, commonName string) *x509.Certificate {
t.Helper()
for _, cert := range loadBundle(t, caBundleFile) {
if cert.Subject.CommonName == commonName && bytes.Equal(cert.RawSubject, cert.RawIssuer) {
return cert
}
}
t.Fatalf("%s is missing self-signed root %q", caBundleFile, commonName)
return nil
}

// Exactly one cross-sign per G5 root must be present. With several,
// ubiquitous bundling would pick among them on tie-breakers alone, which can
// select a digicert.cn variant or a revoked cross-sign.
func TestDigiCertG5RootsHaveSingleCrossSign(t *testing.T) {
intBundle := loadBundle(t, intBundleFile)
for _, tc := range digiCertG5CrossSigns {
t.Run(tc.g5Root, func(t *testing.T) {
g5 := findSelfSignedRoot(t, tc.g5Root)
older := findSelfSignedRoot(t, tc.olderRoot)

var crossSigns []*x509.Certificate
for _, cert := range intBundle {
if bytes.Equal(cert.RawSubject, g5.RawSubject) && bytes.Equal(cert.RawIssuer, older.RawSubject) {
crossSigns = append(crossSigns, cert)
}
}
if len(crossSigns) != 1 {
t.Fatalf("%s has %d %q certificates issued by %q, want 1",
intBundleFile, len(crossSigns), tc.g5Root, tc.olderRoot)
}

crossSign := crossSigns[0]
if got := fmt.Sprintf("%x", sha256.Sum256(crossSign.Raw)); got != tc.fingerprint {
t.Errorf("cross-sign fingerprint = %s, want %s", got, tc.fingerprint)
}
if !bytes.Equal(crossSign.RawSubjectPublicKeyInfo, g5.RawSubjectPublicKeyInfo) {
t.Errorf("cross-sign does not share the %q key", tc.g5Root)
}
if err := crossSign.CheckSignatureFrom(older); err != nil {
t.Errorf("cross-sign is not signed by %q: %v", tc.olderRoot, err)
}
})
}
}

// Compatible (ubiquitous) bundling ranks candidate chains by platform
// ubiquity first, then chain length. Chains for G5-issued CAs must anchor at
// the older root through the cross-sign.
func TestDigiCertG5IssuersAnchorAtOlderRootInUbiquitousChains(t *testing.T) {
savedPlatforms := ubiquity.Platforms
ubiquity.Platforms = nil
t.Cleanup(func() { ubiquity.Platforms = savedPlatforms })
if err := ubiquity.LoadPlatforms(metadataFile); err != nil {
t.Fatal(err)
}

roots := x509.NewCertPool()
for _, cert := range loadBundle(t, caBundleFile) {
roots.AddCert(cert)
}
intermediates := x509.NewCertPool()
intBundle := loadBundle(t, intBundleFile)
for _, cert := range intBundle {
intermediates.AddCert(cert)
}

for _, tc := range digiCertG5CrossSigns {
t.Run(tc.g5Root, func(t *testing.T) {
g5 := findSelfSignedRoot(t, tc.g5Root)
older := findSelfSignedRoot(t, tc.olderRoot)

checked := 0
for _, cert := range intBundle {
if !bytes.Equal(cert.RawIssuer, g5.RawSubject) || bytes.Equal(cert.RawSubject, g5.RawSubject) {
continue
}
checked++
chains, err := cert.Verify(x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
})
if err != nil {
t.Errorf("%q does not verify: %v", cert.Subject.CommonName, err)
continue
}
chains = ubiquity.Filter(chains, ubiquity.ComparePlatformUbiquity)
chains = ubiquity.Filter(chains, ubiquity.CompareSHA2Homogeneity)
chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
for _, chain := range chains {
if anchor := chain[len(chain)-1]; !anchor.Equal(older) {
t.Errorf("ubiquitous chain for %q anchors at %q, want %q",
cert.Subject.CommonName, anchor.Subject.CommonName, tc.olderRoot)
}
}
}
if checked == 0 {
t.Fatalf("%s contains no CAs issued by %q", intBundleFile, tc.g5Root)
}
})
}
}
38 changes: 38 additions & 0 deletions certdata/int-bundle.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23154,6 +23154,44 @@ Details:
1 AIA:
http://yr.i.lencr.org/
CERTIFICATE
Subject: /DigiCert TLS RSA4096 Root G5/C=US/O=DigiCert, Inc.
Issuer: /DigiCert Global Root G2/C=US/O=DigiCert Inc/OU=www.digicert.com
Signature algorithm: RSA / SHA384
Details:
Public key: RSA-4096
Serial number: 10141357722440787320450027978971368270
AKI: 4E:22:54:20:18:95:E6:E3:6E:E6:0F:FA:FA:B9:12:ED:06:17:8F:39
SKI: 51:33:1C:ED:36:40:AF:17:D3:25:CD:69:68:F2:AF:4E:23:3E:B3:41
Valid from: 2026-03-31T00:00:00+0000
until: 2036-03-30T23:59:59+0000
Key usages: cert sign, crl sign, digital signature
Extended usages: client auth, server auth
Basic constraints: valid, is a CA certificate
SANs (0):
1 AIA:
http://cacerts.digicert.com/DigiCertGlobalRootG2.crt
OCSP server:
- http://ocsp.digicert.com
CERTIFICATE
Subject: /DigiCert TLS ECC P384 Root G5/C=US/O=DigiCert, Inc.
Issuer: /DigiCert Global Root G3/C=US/O=DigiCert Inc/OU=www.digicert.com
Signature algorithm: ECDSA / SHA384
Details:
Public key: ECDSA-secp384r1
Serial number: 18539909719201106601661290138344010236
AKI: B3:DB:48:A4:F9:A1:C5:D8:AE:36:41:CC:11:63:69:62:29:BC:4B:C6
SKI: C1:51:45:50:59:AB:3E:E7:2C:5A:FA:20:22:12:07:80:88:7C:11:6A
Valid from: 2026-03-31T00:00:00+0000
until: 2036-03-30T23:59:59+0000
Key usages: cert sign, crl sign, digital signature
Extended usages: client auth, server auth
Basic constraints: valid, is a CA certificate
SANs (0):
1 AIA:
http://cacerts.digicert.com/DigiCertGlobalRootG3.crt
OCSP server:
- http://ocsp.digicert.com
CERTIFICATE
Subject: /Root YE/C=US/O=ISRG
Issuer: /ISRG Root X2/C=US/O=Internet Security Research Group
Signature algorithm: ECDSA / SHA384
Expand Down
52 changes: 52 additions & 0 deletions int-bundle.crt
Original file line number Diff line number Diff line change
Expand Up @@ -38491,6 +38491,58 @@ csOPNIXeJm1YsnJyxUGvKRWjOn+vC9k1SXilnnwhcIs9Pp9e5bckYAnB79VJXN/L
8X8xt/9Qm2xmKKe/xzKA+oRvWKofeFGKU7hcnFbyUwlCu0d+JkgWxQu4x4drcQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFjTCCBHWgAwIBAgIQB6EniiEn8rTEIXDs7HNLTjANBgkqhkiG9w0BAQwFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0yNjAzMzEwMDAwMDBaFw0zNjAzMzAyMzU5NTlaME0xCzAJBgNVBAYTAlVT
MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjElMCMGA1UEAxMcRGlnaUNlcnQgVExT
IFJTQTQwOTYgUm9vdCBHNTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
ALPQ9Ml5EZ39/GaB58zV5LzsgT5qNY4ut+fer/kHTc8wneoJC5m9bFfaGEq4eKw6
OaimSKwucuW96/EazeekA6k/EbTYL4kW+5QBPbsv+BMFoXgcjijgReCD9FkblbOu
fgNF5b7CQv7u8jy2hROYMp0WqCnCCxw43J8xd1y/J6P8J6y3K710mxct8oHaXbDh
Ixc+iEoSI9Dqz53eAxexQkqgFkykbZPpPzruOnydWJ30To/8OyPIbbjiBdrM6+zD
MfTXpylUgM9EW0xvMJ7zzN0flEOdTX9wcA3UOtE38Gydm8AUk1jvzUE4dbwTA5V8
f+Nc6dUN1eJ8EGKqa/A9dvM/o+iwwf3vqldNrIanGLQpwSwOv2S+KYzYAi3NXC/y
f+8V9AwVrAqw8dMNT2pNd5cBoPFmt7fO787s7KV1yqzj4WP3uKEEyLx7P10tFiJW
7UhJ/qcveTAlm7prLT+dO8QX5x0u+/LPpvzjFCyWmCGMtJHpGWCD8jArBnNQ1Zg7
BunHigxgjCj4Uptu4fZNuwYkm9crJj/9Ki9x9dYkvn8xng9t6I9PTaM//zXq30le
QY+G+fF3eUsbtKNeL/tGAtBmE15ehU/O2HCIe84BtZaX1819/YL4wiTBygE5T42i
wRRAH5xm1QwJRtby0NFIdlY6Q8u2ChE5uowTbAa1ns/rAgMBAAGjggFTMIIBTzAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRRMxztNkCvF9MlzWlo8q9OIz6zQTAf
BgNVHSMEGDAWgBROIlQgGJXm427mD/r6uRLtBhePOTAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHYGCCsGAQUFBwEBBGowaDAk
BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEAGCCsGAQUFBzAC
hjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290
RzIuY3J0MEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
bS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0G
CSqGSIb3DQEBDAUAA4IBAQCygk4jhHbNHkudb7jNJyYpa+RPaf4e9RYg7Yen/wrB
QmH7KA1ChcB8vtwgel+sEUexdeL6umaJLU/ZcrnZdl5cLkI7KKxFILSkvlXxBinO
C0mlkhJTsIVJeDJg0/c6RPhkuvvfS3d0rahtxMoNRrBEizXximEoR5R2nc2luBhr
+WCGwEqngw8TMDam4SPtnelkmcSL9FO5qWO2ntQ5DF9pNRRGplOVTr4QU92TMsTb
indNp2Bo7ZKoVdP4G870h4RKD5QMBVJLw1xq6S6MztM6uLjVw1PaylLsKrKLaVAG
Vmno9tEKYqvghBJRWP1fLByEISZXKxwxkW+Damo4HOYm
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDPjCCAsWgAwIBAgIQDfKoH2u+mkjaz/co9nqx/DAKBggqhkjOPQQDAzBhMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
Fw0yNjAzMzEwMDAwMDBaFw0zNjAzMzAyMzU5NTlaME4xCzAJBgNVBAYTAlVTMRcw
FQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEmMCQGA1UEAxMdRGlnaUNlcnQgVExTIEVD
QyBQMzg0IFJvb3QgRzUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATBRKHPEZdQmt4j
gjUHzdDLGJ3S8X93NU873ZRyUu3CO/js+ntrWCDsma7J/GizdbnbCezIE/VOxgod
ZjBMux9HCjxhEEIpfKUIDuAi6dM1aM6bY5+EtZlNWKCO9VTnlcmjggFTMIIBTzAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTBUUVQWas+5yxa+iAiEgeAiHwRajAf
BgNVHSMEGDAWgBSz20ik+aHF2K42QcwRY2liKbxLxjAOBgNVHQ8BAf8EBAMCAYYw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHYGCCsGAQUFBwEBBGowaDAk
BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEAGCCsGAQUFBzAC
hjRodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290
RzMuY3J0MEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNv
bS9EaWdpQ2VydEdsb2JhbFJvb3RHMy5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMAoG
CCqGSM49BAMDA2cAMGQCMEYRbQ/kWYbDzbUVFVbGkSfbuRd8IdZKlBEXeGHSwP1s
/U13YbFSW1tEvEnmqGeklQIwaxS4hzycn7TwA1HRkmGRcyyZeb3my/2O+SkpqLkS
s7foTAkep7TQYkCGv/8AcS+J
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICpjCCAiugAwIBAgIRAIchZfw0tuX7qK3Vs3BftTowCgYIKoZIzj0EAwMwTzEL
MAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNo
IEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDIwHhcNMjYwNTEzMDAwMDAwWhcN
Expand Down
Loading