ca-signed: note self-signed certs.

This commit is contained in:
2025-11-14 14:49:46 -08:00
parent 83c95d9db8
commit 0556c7c56d

View File

@@ -80,6 +80,24 @@ func makePoolFromBytes(data []byte) (*x509.CertPool, error) {
return pool, nil return pool, nil
} }
// isSelfSigned returns true if the given certificate is self-signed.
// It checks that the subject and issuer match and that the certificate's
// signature verifies against its own public key.
func isSelfSigned(cert *x509.Certificate) bool {
if cert == nil {
return false
}
// Quick check: subject and issuer match
if cert.Subject.String() != cert.Issuer.String() {
return false
}
// Cryptographic check: the certificate is signed by itself
if err := cert.CheckSignatureFrom(cert); err != nil {
return false
}
return true
}
func verifyAgainstCA(caPool *x509.CertPool, path string) (ok bool, expiry string) { func verifyAgainstCA(caPool *x509.CertPool, path string) (ok bool, expiry string) {
certs, err := loadCertsFromFile(path) certs, err := loadCertsFromFile(path)
if err != nil || len(certs) == 0 { if err != nil || len(certs) == 0 {
@@ -181,6 +199,30 @@ func selftest() int {
} }
} }
// Verify that both embedded root CAs are detected as self-signed
roots := []string{"testdata/gts-r1.pem", "testdata/isrg-root-x1.pem"}
for _, root := range roots {
b, err := embeddedTestdata.ReadFile(root)
if err != nil {
fmt.Fprintf(os.Stderr, "selftest: failed to read embedded %s: %v\n", root, err)
failures++
continue
}
certs, err := loadCertsFromBytes(b)
if err != nil || len(certs) == 0 {
fmt.Fprintf(os.Stderr, "selftest: failed to parse cert(s) from %s: %v\n", root, err)
failures++
continue
}
leaf := certs[0]
if isSelfSigned(leaf) {
fmt.Printf("%s: SELF-SIGNED (as expected)\n", root)
} else {
fmt.Printf("%s: expected SELF-SIGNED, but was not detected as such\n", root)
failures++
}
}
if failures == 0 { if failures == 0 {
fmt.Println("selftest: PASS") fmt.Println("selftest: PASS")
return 0 return 0
@@ -211,6 +253,18 @@ func main() {
for _, certPath := range os.Args[2:] { for _, certPath := range os.Args[2:] {
ok, exp := verifyAgainstCA(caPool, certPath) ok, exp := verifyAgainstCA(caPool, certPath)
name := filepath.Base(certPath) name := filepath.Base(certPath)
// Load the leaf once for self-signed detection and potential expiry fallback
var leaf *x509.Certificate
if certs, err := loadCertsFromFile(certPath); err == nil && len(certs) > 0 {
leaf = certs[0]
}
// If the certificate is self-signed, prefer the SELF-SIGNED label
if isSelfSigned(leaf) {
fmt.Printf("%s: SELF-SIGNED\n", name)
continue
}
if ok { if ok {
// Display with the requested format // Display with the requested format
// Example: file: OK (expires 2031-01-01) // Example: file: OK (expires 2031-01-01)
@@ -218,8 +272,8 @@ func main() {
// Note: no timezone displayed; date only as per example // Note: no timezone displayed; date only as per example
// If exp ended up empty for some reason, recompute safely // If exp ended up empty for some reason, recompute safely
if exp == "" { if exp == "" {
if certs, err := loadCertsFromFile(certPath); err == nil && len(certs) > 0 { if leaf != nil {
exp = certs[0].NotAfter.Format("2006-01-02") exp = leaf.NotAfter.Format("2006-01-02")
} else { } else {
// fallback to the current date to avoid empty; though shouldn't happen // fallback to the current date to avoid empty; though shouldn't happen
exp = time.Now().Format("2006-01-02") exp = time.Now().Format("2006-01-02")