x509decode

package
v0.783.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

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).

func DecodeCRL added in v0.770.0

func DecodeCRL(input string) (*CRLInfo, error)

DecodeCRL parses an X.509 CRL from PEM ("X509 CRL") or hex-DER input.

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.

func DecodeCSR added in v0.770.0

func DecodeCSR(input string) (*CSRInfo, error)

DecodeCSR parses a PKCS#10 certificate signing request from PEM ("CERTIFICATE REQUEST") or hex-DER input and verifies its self-signature.

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 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

func DecodeOCSP(input string) (*OCSPInfo, error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL