ca-signed: note self-signed certs.
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user