Publish certificate chain data for DANE consumers
Add Chain []CertInfo to TLSProbe, carrying per-cert DER and precomputed TLSA hashes (Cert/SPKI, SHA-256/SHA-512) plus the raw SPKI DER. This lets downstream checkers (checker-dane) perform TLSA matching against the observed chain without re-running a TLS handshake.
This commit is contained in:
parent
ccc5b0cd98
commit
17ecf3beb5
2 changed files with 67 additions and 0 deletions
|
|
@ -2,8 +2,11 @@ package checker
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -15,6 +18,32 @@ import (
|
|||
"git.happydns.org/checker-tls/contract"
|
||||
)
|
||||
|
||||
// buildChain returns CertInfo for each cert presented by the server, in the
|
||||
// order the server sent them (leaf first). SPKI is extracted from the parsed
|
||||
// certificate's RawSubjectPublicKeyInfo so we hash exactly the DER bytes
|
||||
// DANE selector 1 refers to (RFC 6698 §1.1.3).
|
||||
func buildChain(certs []*x509.Certificate) []CertInfo {
|
||||
out := make([]CertInfo, len(certs))
|
||||
for i, c := range certs {
|
||||
certSum256 := sha256.Sum256(c.Raw)
|
||||
certSum512 := sha512.Sum512(c.Raw)
|
||||
spkiSum256 := sha256.Sum256(c.RawSubjectPublicKeyInfo)
|
||||
spkiSum512 := sha512.Sum512(c.RawSubjectPublicKeyInfo)
|
||||
out[i] = CertInfo{
|
||||
DERBase64: base64.StdEncoding.EncodeToString(c.Raw),
|
||||
Subject: c.Subject.String(),
|
||||
Issuer: c.Issuer.String(),
|
||||
NotAfter: c.NotAfter,
|
||||
CertSHA256: hex.EncodeToString(certSum256[:]),
|
||||
CertSHA512: hex.EncodeToString(certSum512[:]),
|
||||
SPKISHA256: hex.EncodeToString(spkiSum256[:]),
|
||||
SPKISHA512: hex.EncodeToString(spkiSum512[:]),
|
||||
SPKIDERBase64: base64.StdEncoding.EncodeToString(c.RawSubjectPublicKeyInfo),
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// probeTypeString renders the TLSProbe.Type string from a TLSEndpoint.
|
||||
// Observation consumers already parse this field in its "tls" /
|
||||
// "starttls-<proto>" shape; the contract-level split of direct vs.
|
||||
|
|
@ -102,6 +131,7 @@ func probe(ctx context.Context, ep contract.TLSEndpoint, timeout time.Duration)
|
|||
}
|
||||
p.Subject = leaf.Subject.CommonName
|
||||
p.DNSNames = append(p.DNSNames, leaf.DNSNames...)
|
||||
p.Chain = buildChain(state.PeerCertificates)
|
||||
|
||||
hostnameMatch := leaf.VerifyHostname(sni) == nil
|
||||
p.HostnameMatch = &hostnameMatch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue