Add all other cert details
This commit is contained in:
@@ -5,6 +5,7 @@ package utils
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
@@ -74,11 +75,63 @@ type IssuerInfo struct {
|
||||
SerialNumber string `json:"serial_number"`
|
||||
}
|
||||
|
||||
// CertDetails holds fields parsed from the certificate beyond what TrimmedEntry provides.
|
||||
type CertDetails struct {
|
||||
NotBefore string `json:"not_before"`
|
||||
NotAfter string `json:"not_after"`
|
||||
SerialNumber string `json:"serial_number"`
|
||||
Issuer string `json:"issuer"`
|
||||
EmailSANs []string `json:"email_sans,omitempty"`
|
||||
URISANs []string `json:"uri_sans,omitempty"`
|
||||
SubjectKeyID string `json:"subject_key_id,omitempty"`
|
||||
AuthorityKeyID string `json:"authority_key_id,omitempty"`
|
||||
OCSPServers []string `json:"ocsp_servers,omitempty"`
|
||||
IssuingCertURLs []string `json:"issuing_cert_urls,omitempty"`
|
||||
CRLDistributionPoints []string `json:"crl_distribution_points,omitempty"`
|
||||
KeyUsage []string `json:"key_usage,omitempty"`
|
||||
ExtKeyUsage []string `json:"ext_key_usage,omitempty"`
|
||||
IsCA bool `json:"is_ca"`
|
||||
}
|
||||
|
||||
var keyUsageNames = []struct {
|
||||
usage x509.KeyUsage
|
||||
name string
|
||||
}{
|
||||
{x509.KeyUsageDigitalSignature, "DigitalSignature"},
|
||||
{x509.KeyUsageContentCommitment, "ContentCommitment"},
|
||||
{x509.KeyUsageKeyEncipherment, "KeyEncipherment"},
|
||||
{x509.KeyUsageDataEncipherment, "DataEncipherment"},
|
||||
{x509.KeyUsageKeyAgreement, "KeyAgreement"},
|
||||
{x509.KeyUsageCertSign, "CertSign"},
|
||||
{x509.KeyUsageCRLSign, "CRLSign"},
|
||||
{x509.KeyUsageEncipherOnly, "EncipherOnly"},
|
||||
{x509.KeyUsageDecipherOnly, "DecipherOnly"},
|
||||
}
|
||||
|
||||
var extKeyUsageNames = map[x509.ExtKeyUsage]string{
|
||||
x509.ExtKeyUsageAny: "Any",
|
||||
x509.ExtKeyUsageServerAuth: "ServerAuth",
|
||||
x509.ExtKeyUsageClientAuth: "ClientAuth",
|
||||
x509.ExtKeyUsageCodeSigning: "CodeSigning",
|
||||
x509.ExtKeyUsageEmailProtection: "EmailProtection",
|
||||
x509.ExtKeyUsageIPSECEndSystem: "IPSECEndSystem",
|
||||
x509.ExtKeyUsageIPSECTunnel: "IPSECTunnel",
|
||||
x509.ExtKeyUsageIPSECUser: "IPSECUser",
|
||||
x509.ExtKeyUsageTimeStamping: "TimeStamping",
|
||||
x509.ExtKeyUsageOCSPSigning: "OCSPSigning",
|
||||
x509.ExtKeyUsageMicrosoftServerGatedCrypto: "MicrosoftServerGatedCrypto",
|
||||
x509.ExtKeyUsageNetscapeServerGatedCrypto: "NetscapeServerGatedCrypto",
|
||||
x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "MicrosoftCommercialCodeSigning",
|
||||
x509.ExtKeyUsageMicrosoftKernelCodeSigning: "MicrosoftKernelCodeSigning",
|
||||
}
|
||||
|
||||
// Entry represents a CT log entry in JSON format.
|
||||
type Entry struct {
|
||||
EntryNumber int `json:"entry_number"`
|
||||
LeafIndex int64 `json:"leaf_index"`
|
||||
MerkleLeafHash string `json:"merkle_leaf_hash"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TimestampHuman string `json:"timestamp_human"`
|
||||
IsPrecert bool `json:"is_precert"`
|
||||
IssuerKeyHash string `json:"issuer_key_hash,omitempty"`
|
||||
CertificateSize int `json:"certificate_size"`
|
||||
@@ -86,6 +139,7 @@ type Entry struct {
|
||||
ChainFingerprints []string `json:"chain_fingerprints"`
|
||||
Issuers []IssuerInfo `json:"issuers,omitempty"`
|
||||
SCTs []SCT `json:"scts,omitempty"`
|
||||
CertDetails *CertDetails `json:"cert_details,omitempty"`
|
||||
ParsedCertInfo json.RawMessage `json:"parsed_cert_info,omitempty"`
|
||||
}
|
||||
|
||||
@@ -414,11 +468,64 @@ func fetchIssuer(logURL, fingerprint string) (*IssuerInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// parseCertDetails extracts certificate fields not covered by TrimmedEntry.
|
||||
func parseCertDetails(certDER []byte) *CertDetails {
|
||||
cert, err := x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
d := &CertDetails{
|
||||
NotBefore: cert.NotBefore.UTC().Format(time.RFC3339),
|
||||
NotAfter: cert.NotAfter.UTC().Format(time.RFC3339),
|
||||
SerialNumber: cert.SerialNumber.String(),
|
||||
Issuer: cert.Issuer.String(),
|
||||
OCSPServers: cert.OCSPServer,
|
||||
IssuingCertURLs: cert.IssuingCertificateURL,
|
||||
CRLDistributionPoints: cert.CRLDistributionPoints,
|
||||
IsCA: cert.IsCA,
|
||||
}
|
||||
|
||||
for _, e := range cert.EmailAddresses {
|
||||
d.EmailSANs = append(d.EmailSANs, e)
|
||||
}
|
||||
for _, u := range cert.URIs {
|
||||
d.URISANs = append(d.URISANs, u.String())
|
||||
}
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
d.SubjectKeyID = hex.EncodeToString(cert.SubjectKeyId)
|
||||
}
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
d.AuthorityKeyID = hex.EncodeToString(cert.AuthorityKeyId)
|
||||
}
|
||||
for _, ku := range keyUsageNames {
|
||||
if cert.KeyUsage&ku.usage != 0 {
|
||||
d.KeyUsage = append(d.KeyUsage, ku.name)
|
||||
}
|
||||
}
|
||||
for _, eku := range cert.ExtKeyUsage {
|
||||
if name, ok := extKeyUsageNames[eku]; ok {
|
||||
d.ExtKeyUsage = append(d.ExtKeyUsage, name)
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// merkleLeafHash computes the RFC 6962 Merkle leaf hash: SHA-256(0x00 || leaf).
|
||||
func merkleLeafHash(leaf []byte) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte{0x00})
|
||||
h.Write(leaf)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func convertEntry(e *sunlight.LogEntry, entryNum int, opts Options) Entry {
|
||||
entry := Entry{
|
||||
EntryNumber: entryNum,
|
||||
LeafIndex: e.LeafIndex,
|
||||
MerkleLeafHash: merkleLeafHash(e.MerkleTreeLeaf()),
|
||||
Timestamp: e.Timestamp,
|
||||
TimestampHuman: time.UnixMilli(e.Timestamp).UTC().Format(time.RFC3339),
|
||||
IsPrecert: e.IsPrecert,
|
||||
CertificateSize: len(e.Certificate),
|
||||
}
|
||||
@@ -462,6 +569,15 @@ func convertEntry(e *sunlight.LogEntry, entryNum int, opts Options) Entry {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse extended certificate details.
|
||||
certDER := e.Certificate
|
||||
if e.IsPrecert {
|
||||
certDER = e.PreCertificate
|
||||
}
|
||||
if len(certDER) > 0 {
|
||||
entry.CertDetails = parseCertDetails(certDER)
|
||||
}
|
||||
|
||||
// Try to extract parsed certificate info
|
||||
if trimmed, err := e.TrimmedEntry(); err == nil {
|
||||
if data, err := json.Marshal(trimmed); err == nil {
|
||||
|
||||
Reference in New Issue
Block a user