Skip to content

Commit 6cb83c8

Browse files
authored
Merge pull request #11 from dolmen-go/add-certificates-checks
Add certificates checks
2 parents 3f06125 + 49e0413 commit 6cb83c8

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

.github/workflows/update.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ jobs:
2121
- name: Update Mozilla Included CA Certificate List
2222
run: go generate .
2323

24+
- name: Run checks of Mozilla Included CA Certificate List
25+
run: go test -v ./...
26+
2427
- name: Commit changes
2528
id: commit_changes
2629
uses: EndBug/add-and-commit@v9

embedded/rootcerts_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package embedded_test
2+
3+
import (
4+
"crypto/x509"
5+
"encoding/pem"
6+
"testing"
7+
"time"
8+
9+
"github.com/breml/rootcerts/embedded"
10+
)
11+
12+
func parsePEM(pemCerts []byte) (certs []*x509.Certificate, err error) {
13+
for len(pemCerts) > 0 {
14+
var block *pem.Block
15+
16+
block, pemCerts = pem.Decode(pemCerts)
17+
if block == nil {
18+
break
19+
}
20+
21+
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
22+
continue
23+
}
24+
25+
cert, err := x509.ParseCertificate(block.Bytes)
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
certs = append(certs, cert)
31+
}
32+
return
33+
}
34+
35+
func checkRootCertsPEM(t *testing.T, pemCerts []byte, whenFail time.Time, whenWarn time.Time) (ok bool) {
36+
now := time.Now()
37+
t.Logf("Checking certificate validity on %s...", whenFail)
38+
certs, err := parsePEM(pemCerts)
39+
if err != nil {
40+
t.Error(err)
41+
return false
42+
}
43+
44+
roots := x509.NewCertPool()
45+
for _, cert := range certs {
46+
roots.AddCert(cert)
47+
}
48+
49+
var minExpires time.Time
50+
var minExpiresName string
51+
ok = true
52+
for _, cert := range certs {
53+
name := cert.Subject.CommonName
54+
if name == "" {
55+
name = cert.Subject.String() + " (⚠️ missing CommonName)"
56+
if name == "" {
57+
name = cert.Issuer.String()
58+
}
59+
}
60+
61+
if !cert.IsCA {
62+
t.Errorf("❌ %s: not a certificate authority", name)
63+
}
64+
65+
const keyUsageExpected = x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageDigitalSignature
66+
if (cert.KeyUsage &^ keyUsageExpected) != 0 {
67+
t.Logf("⚠️ %s: unexpected key usage %#x (expecting %#x, see constants at https://pkg.go.dev/crypto/x509#KeyUsage)", name, cert.KeyUsage, keyUsageExpected)
68+
}
69+
70+
if minExpires.IsZero() || cert.NotAfter.Before(minExpires) {
71+
minExpires = cert.NotAfter
72+
minExpiresName = name
73+
}
74+
75+
// Check that the certificate is valid now
76+
if cert.NotBefore.After(now) {
77+
t.Errorf("❌ %s: fails NotBefore check: %s", name, cert.NotBefore)
78+
continue
79+
}
80+
81+
// ... and that it will still be valid later
82+
if cert.NotAfter.Before(whenFail) {
83+
t.Errorf("❌ %s: fails NotAfter check: %s", name, cert.NotAfter)
84+
continue
85+
}
86+
87+
if cert.NotAfter.Before(whenWarn) {
88+
t.Logf("⚠️ %s: fails NotAfter check: %s", name, cert.NotAfter)
89+
}
90+
91+
_, err := cert.Verify(x509.VerifyOptions{
92+
Roots: roots,
93+
CurrentTime: whenFail,
94+
})
95+
if err != nil {
96+
t.Errorf("❌ %s: %s", name, err)
97+
ok = false
98+
continue
99+
}
100+
101+
t.Logf("✅ %s (expires: %s)", name, cert.NotAfter)
102+
}
103+
104+
if ok {
105+
t.Log("Success.")
106+
t.Logf("MinExpire: %s (Certificate: %s)", minExpires, minExpiresName)
107+
}
108+
109+
return
110+
}
111+
112+
func TestCerts(t *testing.T) {
113+
// Check that certificates will still be valid in 1 month, warn if invalid in 3 months
114+
checkRootCertsPEM(t, []byte(embedded.MozillaCACertificatesPEM()), time.Now().AddDate(0, 1, 0), time.Now().AddDate(0, 3, 0))
115+
116+
// Should fail
117+
// checkRootCertsPEM(t, []byte(embedded.MozillaCACertificatesPEM()), time.Now().AddDate(20, 0, 0), time.Now().AddDate(30, 0, 0))
118+
}

0 commit comments

Comments
 (0)