Skip to content

Commit 439fb7e

Browse files
committed
Add checks of certificates validity.
Add checks of validity of the certificates in the near future (1 month from now). Add checks of the certificates declared usage.
1 parent 3f06125 commit 439fb7e

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

embedded/rootcerts_test.go

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

0 commit comments

Comments
 (0)