Documentation
¶
Overview ¶
Package x509decode parses X.509 certificates into a structured view — the natural complement to tls_handshake_decode (whose Certificate handshake-message body is surfaced as raw hex). Operators paste a PEM or DER blob and inspect subject, issuer, validity, SANs, key usage, EKU, AIA, CRL distribution points, and fingerprints without dragging out `openssl x509 -text` or pulling the cert into a separate inspection tool.
Wrap-vs-native judgement ¶
Native — via the Go standard library's crypto/x509 + encoding/pem packages. The X.509 v3 format is defined by RFC 5280 + supporting RFCs (CT, SCTs, ACME, etc.); stdlib handles the recursive ASN.1 DER walk so this package can focus on rendering the parsed fields into the same JSON shape every other native-fit decoder in this codebase uses. No vendor SDK, no networking, no cryptographic operations beyond computing the SHA-1 + SHA-256 fingerprints — well within the stdlib scope.
What this package covers ¶
- PEM and DER input auto-detection: input starting with "-----BEGIN CERTIFICATE-----" is decoded as PEM (with base64 unwrap and chain support — the first cert in the chain is decoded; subsequent certs are exposed via a count); everything else is treated as hex-encoded DER.
- Subject + Issuer Distinguished Name: each RDN (CommonName, Organization, OrganizationalUnit, Country, Province, Locality, StreetAddress, PostalCode, SerialNumber) is surfaced as both a flat string and the full DN as a canonical openssl-style string.
- Serial number rendered as both decimal and uppercase hex (the form printed by every certificate UI).
- Validity window: NotBefore + NotAfter as RFC 3339 timestamps + a `days_remaining` count for quick expiration triage (negative when already expired).
- Public key algorithm + key size:
- RSA: modulus size in bits (1024 / 2048 / 4096 / etc.).
- ECDSA: curve name (P-256 / P-384 / P-521).
- Ed25519 / Ed448: marked by name.
- DSA: modulus size.
- Signature algorithm name (SHA1-RSA / SHA256-RSA / SHA- 256-ECDSA / SHA256-RSA-PSS / Ed25519 / etc.).
- X.509 version (v1 / v2 / v3).
- Extensions:
- Subject Alternative Names (DNS / IP / email / URI).
- Key Usage (digital signature / key encipherment / cert signing / etc.).
- Extended Key Usage (server auth / client auth / code signing / email protection / OCSP signing / time stamping / etc.).
- Basic Constraints (CA flag + optional path length).
- Authority Information Access (OCSP responder URLs + CA Issuer URLs).
- CRL Distribution Points (URLs).
- Subject Key Identifier (SKI, hex-encoded).
- Authority Key Identifier (AKI, hex-encoded).
- Certificate Policies (OIDs).
- Fingerprints: SHA-1 (legacy / GUI-displayed), SHA-256 (modern / SPKI pinning).
- **JA4X fingerprint** (FoxIO): the certificate member of the JA4+ family — hash12(issuer RDN OIDs) _ hash12(subject RDN OIDs) _ hash12(extension OIDs), each the comma-joined hex of the OID DER content-octets in certificate order. Fingerprints the cert-generation stack (malware C2 / phishing infra reuses it). Verified byte-for-byte against FoxIO snapshot RDN hashes.
What this package does NOT cover (deliberately out of scope) ¶
- Chain validation (signature verification, trust-store traversal, revocation checks) — pure decoding only. The caller can wire a follow-up Spec that walks a decoded chain against a configured trust store.
- Certificate Transparency SCT decoding (SCT list extension is recognised by OID but the body is surfaced as raw hex; SCT v1 binary format is a separate ~200 LoC walker).
- CSR (Certificate Signing Request) parsing — that's a different ASN.1 structure; a future Spec can cover it.
- CRL (Certificate Revocation List) parsing — separate iteration, different ASN.1 structure.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CRLInfo ¶ added in v0.770.0
type CRLInfo struct {
Source string `json:"source"` // PEM | DER
IssuerDN string `json:"issuer_dn"`
Issuer *Name `json:"issuer"`
ThisUpdate string `json:"this_update"`
NextUpdate string `json:"next_update,omitempty"`
Expired bool `json:"expired"` // NextUpdate is in the past
CRLNumber string `json:"crl_number,omitempty"`
SignatureAlgorithm string `json:"signature_algorithm"`
AuthorityKeyID string `json:"authority_key_id_hex,omitempty"`
RevokedCount int `json:"revoked_count"`
RevokedSerials []string `json:"revoked_serials,omitempty"` // hex, capped
RevokedTruncated bool `json:"revoked_truncated,omitempty"`
}
CRLInfo is the decoded view of an X.509 Certificate Revocation List (RFC 5280).
type CSRInfo ¶ added in v0.770.0
type CSRInfo struct {
Source string `json:"source"` // PEM | DER
SubjectDN string `json:"subject_dn"`
Subject *Name `json:"subject"`
PublicKeyAlgorithm string `json:"public_key_algorithm"`
PublicKeyDetails string `json:"public_key_details"`
SignatureAlgorithm string `json:"signature_algorithm"`
// Requested subject-alternative names.
DNSNames []string `json:"dns_names,omitempty"`
IPAddresses []string `json:"ip_addresses,omitempty"`
EmailAddresses []string `json:"email_addresses,omitempty"`
URIs []string `json:"uris,omitempty"`
// SignatureValid reports whether the CSR's self-signature verifies
// against its own embedded public key — proof the requester possesses
// the matching private key. A false here on a real enrollment request is
// a red flag (tampered or forged request).
SignatureValid bool `json:"signature_valid"`
SignatureError string `json:"signature_error,omitempty"`
FingerprintSHA256 string `json:"fingerprint_sha256"` // of the DER
}
CSRInfo is the decoded view of a PKCS#10 certificate signing request (RFC 2986) — the enrollment request a client/device submits to a CA.
type Certificate ¶
type Certificate struct {
Source string `json:"source"`
Version int `json:"version"`
SerialNumberHex string `json:"serial_number_hex"`
SerialNumberDec string `json:"serial_number_decimal"`
SubjectDN string `json:"subject_dn"`
Subject *Name `json:"subject"`
IssuerDN string `json:"issuer_dn"`
Issuer *Name `json:"issuer"`
NotBefore string `json:"not_before"`
NotAfter string `json:"not_after"`
DaysRemaining int `json:"days_remaining"`
Expired bool `json:"expired"`
PublicKeyAlgorithm string `json:"public_key_algorithm"`
PublicKeyDetails string `json:"public_key_details"`
SignatureAlgorithm string `json:"signature_algorithm"`
Extensions *Extensions `json:"extensions,omitempty"`
FingerprintSHA1 string `json:"fingerprint_sha1"`
FingerprintSHA256 string `json:"fingerprint_sha256"`
SelfSigned bool `json:"self_signed"`
IsCA bool `json:"is_ca"`
ChainLength int `json:"chain_length_seen,omitempty"`
JA4X string `json:"ja4x,omitempty"`
}
Certificate is the decoded view of one X.509 v3 certificate.
func Decode ¶
func Decode(input string) (*Certificate, error)
Decode parses a PEM or hex-DER certificate input.
PEM input is detected by the "-----BEGIN" prefix. For PEM chains the first certificate is decoded and the total chain length is reported via ChainLength.
type ChainCert ¶ added in v0.772.0
type ChainCert struct {
Position int `json:"position"` // 0 = leaf
SubjectDN string `json:"subject_dn"`
IssuerDN string `json:"issuer_dn"`
SelfIssued bool `json:"self_issued"` // subject == issuer
IsCA bool `json:"is_ca"`
NotAfter string `json:"not_after"`
Expired bool `json:"expired"`
}
ChainCert is the per-certificate summary in a verified chain.
type ChainLink ¶ added in v0.772.0
type ChainLink struct {
ChildPosition int `json:"child_position"`
ParentPosition int `json:"parent_position"`
ChildSubject string `json:"child_subject"`
ParentSubject string `json:"parent_subject"`
Valid bool `json:"valid"`
Error string `json:"error,omitempty"`
}
ChainLink reports whether certificate i is validly signed by certificate i+1 (the candidate parent immediately above it in the supplied order).
type ChainResult ¶ added in v0.772.0
type ChainResult struct {
Source string `json:"source"` // PEM | DER
Count int `json:"count"`
Certs []ChainCert `json:"certs"`
Links []ChainLink `json:"links,omitempty"`
// Ordered is true when every adjacent link verifies, i.e. the certs are
// in leaf -> ... -> root order and each is signed by the next.
Ordered bool `json:"ordered"`
// ReachesSelfSignedRoot is true when the last certificate is self-issued
// and its own signature verifies (a trust-anchor root is present at the
// end of the chain).
ReachesSelfSignedRoot bool `json:"reaches_self_signed_root"`
// AnyExpired flags whether any certificate in the chain is past its
// NotAfter — a common cause of "chain looks right but is rejected".
AnyExpired bool `json:"any_expired"`
Note string `json:"note"`
}
ChainResult is the decoded + linkage-verified view of a certificate chain.
func VerifyChain ¶ added in v0.772.0
func VerifyChain(input string) (*ChainResult, error)
VerifyChain parses every certificate in a PEM bundle (or a single hex-DER certificate) and checks the signature linkage between adjacent certificates in the order supplied: each certificate must be signed by the next one up. It reports ordering, whether a self-signed root terminates the chain, and per-certificate expiry — the information an operator needs to diagnose the usual "the chain is present but not trusted" failures (wrong order, missing intermediate, expired link).
Linkage uses crypto/x509's CheckSignatureFrom, which verifies the cryptographic signature AND that the parent is a CA permitted to sign certificates. It does NOT perform full RFC 5280 path validation (name constraints, policies, or trust against a root store) — expiry is reported per certificate but is not folded into link validity.
type Extensions ¶
type Extensions struct {
DNSNames []string `json:"dns_names,omitempty"`
IPAddresses []string `json:"ip_addresses,omitempty"`
EmailAddresses []string `json:"email_addresses,omitempty"`
URIs []string `json:"uris,omitempty"`
KeyUsage []string `json:"key_usage,omitempty"`
ExtendedKeyUsage []string `json:"extended_key_usage,omitempty"`
BasicConstraintsValid bool `json:"basic_constraints_valid"`
IsCA bool `json:"is_ca,omitempty"`
MaxPathLen int `json:"max_path_len,omitempty"`
MaxPathLenZero bool `json:"max_path_len_zero,omitempty"`
OCSPServers []string `json:"ocsp_servers,omitempty"`
IssuingCertificateURLs []string `json:"issuing_certificate_urls,omitempty"`
CRLDistributionPoints []string `json:"crl_distribution_points,omitempty"`
SubjectKeyID string `json:"subject_key_id_hex,omitempty"`
AuthorityKeyID string `json:"authority_key_id_hex,omitempty"`
PolicyOIDs []string `json:"policy_oids,omitempty"`
}
Extensions carries the operationally-interesting v3 extensions.
type Name ¶
type Name struct {
CommonName string `json:"common_name,omitempty"`
Country []string `json:"country,omitempty"`
Organization []string `json:"organization,omitempty"`
OrganizationalUnit []string `json:"organizational_unit,omitempty"`
Locality []string `json:"locality,omitempty"`
Province []string `json:"province,omitempty"`
StreetAddress []string `json:"street_address,omitempty"`
PostalCode []string `json:"postal_code,omitempty"`
SerialNumber string `json:"serial_number,omitempty"`
}
Name is the structured view of a DN.
type OCSPInfo ¶ added in v0.771.0
type OCSPInfo struct {
Status string `json:"status"` // good | revoked | unknown | server_failed | unknown(N)
SerialNumberHex string `json:"serial_number_hex,omitempty"`
ProducedAt string `json:"produced_at,omitempty"`
ThisUpdate string `json:"this_update,omitempty"`
NextUpdate string `json:"next_update,omitempty"`
Expired bool `json:"expired"` // NextUpdate is in the past
RevokedAt string `json:"revoked_at,omitempty"`
RevocationReason string `json:"revocation_reason,omitempty"` // only when revoked
SignatureAlgorithm string `json:"signature_algorithm,omitempty"`
// Responder identity: exactly one of a DN (byName) or a key hash (byKey),
// or the embedded responder certificate's subject when present.
ResponderName string `json:"responder_name,omitempty"`
ResponderKeyHashHex string `json:"responder_key_hash_hex,omitempty"`
Note string `json:"note"`
}
OCSPInfo is the decoded view of an OCSP response (RFC 6960) — the query-based revocation answer (good / revoked / unknown) for a single certificate, the per-certificate counterpart to a CRL's list.
func DecodeOCSP ¶ added in v0.771.0
DecodeOCSP parses an OCSP response from base64 (the usual HTTP/captured form) or hex-encoded DER. The signature is NOT verified — that needs the issuer certificate an operator inspecting a captured response won't have — so the result carries an explicit not-verified note. Responses with an unsuccessful outer status (malformed / internalError / tryLater / unauthorized) surface as the parse error from x/crypto/ocsp.