certkit

package module
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Feb 15, 2026 License: MIT Imports: 33 Imported by: 0

README

certkit

A Swiss Army knife for TLS/SSL certificates. Inspect, verify, bundle, scan, and generate certificates and keys -- all from a single tool.

What can it do?

  • Inspect any certificate, key, or CSR and see exactly what's in it
  • Verify that a cert chains to a trusted root, matches its key, and isn't about to expire
  • Bundle a leaf cert into a full chain for your web server (nginx, Apache, HAProxy, etc.)
  • Scan a directory full of certs and keys to understand what you have
  • Generate new key pairs and CSRs for certificate renewals
  • Convert between formats -- PEM, PKCS#12, JKS, PKCS#7, DER, Kubernetes Secrets

Works with every common format out of the box. No OpenSSL gymnastics required.

Install

Homebrew (macOS)
brew install sensiblebit/tap/certkit
Debian/Ubuntu (Linux)

Download the .deb package from the latest release and install:

sudo dpkg -i certkit_*.deb
From source

Requires Go 1.25+.

go build -o certkit ./cmd/certkit/

Quick Start

See what's in a certificate:

certkit inspect cert.pem

Check if it's valid and not expiring soon:

certkit verify cert.pem --expiry 30d

Build the full chain your web server needs:

certkit bundle cert.pem -o chain.pem

See EXAMPLES.md for a complete walkthrough of every command with real-world scenarios.

Commands

Command What it does
certkit inspect <file> Show what's in a cert, key, or CSR
certkit verify <file> Check chain, key match, and expiry
certkit bundle <file> Build a certificate chain from a leaf cert
certkit scan <path> Scan a directory and catalog everything found
certkit keygen Generate a new key pair (and optionally a CSR)
certkit csr Generate a CSR from a template, cert, or existing CSR

License

MIT


Reference

Global Flags
Flag Default Description
--log-level, -l info Log level: debug, info, warn, error
--passwords, -p Comma-separated passwords for encrypted keys
--password-file File containing passwords, one per line
--allow-expired false Include expired certificates

Common passwords ("", "password", "changeit", "keypassword") are always tried automatically.

Inspect Flags
Flag Default Description
--format text Output format: text, json
Verify Flags
Flag Default Description
--key Private key file to check against the certificate
--expiry, -e Check if cert expires within duration (e.g., 30d, 720h)
--trust-store mozilla Trust store: system, mozilla
--format text Output format: text, json

Chain verification is always performed. When the input contains an embedded private key (PKCS#12, JKS), key match is checked automatically.

Bundle Flags
Flag Default Description
--key Private key file (PEM)
--out-file, -o (stdout) Output file
--format pem Output format: pem, chain, fullchain, p12, jks
--force, -f false Skip chain verification
--trust-store mozilla Trust store: system, mozilla
Scan Flags
Flag Default Description
--db, -d (in-memory) SQLite database path
--bundle-path Export bundles to this directory
--config, -c ./bundles.yaml Path to bundle config YAML
--force, -f false Allow export of untrusted certificate bundles
--duplicates false Export all certificates per bundle, not just the newest
--dump-keys Dump all discovered keys to a single PEM file
--dump-certs Dump all discovered certificates to a single PEM file
--max-file-size 10485760 Skip files larger than this size in bytes (0 to disable)
--format text Output format: text, json
Keygen Flags
Flag Default Description
--algorithm, -a ecdsa Key algorithm: rsa, ecdsa, ed25519
--bits, -b 4096 RSA key size in bits
--curve P-256 ECDSA curve: P-256, P-384, P-521
--out-path, -o (stdout) Output directory
--cn Common Name (triggers CSR generation)
--sans Comma-separated SANs (triggers CSR generation)
CSR Flags
Flag Default Description
--template JSON template file for CSR generation
--cert PEM certificate to use as CSR template
--from-csr Existing PEM CSR to re-sign with a new key
--key Existing private key file (PEM); generates new if omitted
--algorithm, -a ecdsa Key algorithm for generated keys
--bits, -b 4096 RSA key size in bits
--curve P-256 ECDSA curve
--out-path, -o (stdout) Output directory

Exactly one of --template, --cert, or --from-csr is required.

Exit Codes
Code Meaning
0 Success
1 General error (bad input, missing file, etc.)
2 Validation failure (chain invalid, key mismatch, expired)
Bundle Configuration

Bundles are defined in a YAML file that maps certificate Common Names to named bundles. An optional defaultSubject provides fallback X.509 subject fields for CSR generation.

defaultSubject:
  country: [US]
  province: [California]
  locality: [San Diego]
  organization: [Company, Inc.]
  organizationalUnit: [DevOps]

bundles:
  - bundleName: examplecom-tls
    commonNames:
      - '*.example.com'
      - example.com

  - bundleName: exampleio-tls
    commonNames:
      - '*.example.io'
      - example.io
    subject:  # overrides defaultSubject for this bundle
      country: [GB]
      province: [London]
      locality: [London]
      organization: [Company UK, Ltd.]
      organizationalUnit: [Platform Engineering]

Bundles without an explicit subject block inherit from defaultSubject. Certificate-to-bundle matching uses exact Common Name comparison against the commonNames list (a CN of *.example.com matches the literal wildcard string, not subdomains).

Bundle Output Files

When running certkit scan --bundle-path, each bundle produces the following files under <dir>/<bundleName>/:

File Contents
<cn>.pem Leaf certificate
<cn>.chain.pem Leaf + intermediates
<cn>.fullchain.pem Leaf + intermediates + root
<cn>.intermediates.pem Intermediate certificates
<cn>.root.pem Root certificate
<cn>.key Private key (PEM, mode 0600)
<cn>.p12 PKCS#12 archive (password: changeit, mode 0600)
<cn>.k8s.yaml Kubernetes kubernetes.io/tls Secret (mode 0600)
<cn>.json Certificate metadata
<cn>.yaml Certificate and key metadata
<cn>.csr Certificate Signing Request
<cn>.csr.json CSR details (subject, SANs, key algorithm)

Wildcard characters in the CN are replaced with _ in filenames (e.g., *.example.com becomes _.example.com). The .intermediates.pem and .root.pem files are only created when those certificates exist in the chain.

Library

The certkit Go package provides reusable certificate utilities:

import "github.com/sensiblebit/certkit"

// Parse certificates and keys
certs, _ := certkit.ParsePEMCertificates(pemData)
key, _ := certkit.ParsePEMPrivateKey(keyPEM)

// Compute identifiers
fingerprint := certkit.CertFingerprint(cert)
colonFP := certkit.CertFingerprintColonSHA256(cert)  // AA:BB:CC format
ski := certkit.CertSKI(cert)

// Check expiry
if certkit.CertExpiresWithin(cert, 30*24*time.Hour) {
    // cert expires within 30 days
}

// Build verified chains (library defaults to system trust store)
opts := certkit.DefaultOptions()
opts.TrustStore = "mozilla" // or "system" (the default)
bundle, _ := certkit.Bundle(ctx, leaf, opts)

// Generate keys
ecKey, _ := certkit.GenerateECKey(elliptic.P256())
rsaKey, _ := certkit.GenerateRSAKey(4096)

// Generate CSRs
csrPEM, keyPEM, _ := certkit.GenerateCSR(leaf, nil) // auto-generates EC P-256 key

// PKCS operations
p12, _ := certkit.EncodePKCS12(key, leaf, intermediates, "password")
p7, _ := certkit.EncodePKCS7(certs)
jks, _ := certkit.EncodeJKS(key, leaf, intermediates, "changeit")
How It Works
flowchart TD
    A[Input files / stdin] --> B[Format detection - PEM vs DER]
    B --> C[Parse certs, keys, CSRs<br/>PKCS#12, PKCS#7, JKS, encrypted PEM, PKCS#8, SEC1, Ed25519]
    C --> D[Store in SQLite<br/>certificates + keys indexed by SKI]
    D --> E[Resolve AKIs<br/>match legacy SHA-1 AKIs to computed RFC 7093 M1 SKIs]
    E --> F{--bundle-path?}
    F -- yes --> G[Match keys to certs, build chains,<br/>write all output formats per bundle]
    F -- no --> H[Print scan summary]

Expired certificates are skipped during ingestion by default (use --allow-expired to include them). SKI computation uses RFC 7093 Method 1 (SHA-256 truncated to 160 bits). Non-root certificate AKIs are resolved post-ingestion by matching against a multi-hash lookup (RFC 7093 M1 + legacy SHA-1) of all CA certificates.

Documentation

Overview

Package certkit provides certificate parsing, encoding, identification, chain bundling, PKCS#12/7, and CSR generation utilities.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CertAKIEmbedded added in v0.2.1

func CertAKIEmbedded(cert *x509.Certificate) string

CertAKIEmbedded returns the Authority Key Identifier as stored in the certificate extension, as a colon-separated hex string. This matches the issuing CA's embedded SKI and may be SHA-1 or SHA-256. Returns empty string if the extension is not present.

func CertExpiresWithin

func CertExpiresWithin(cert *x509.Certificate, d time.Duration) bool

CertExpiresWithin reports whether the certificate will expire within the given duration from now.

func CertFingerprint

func CertFingerprint(cert *x509.Certificate) string

CertFingerprint returns the SHA-256 fingerprint of a certificate as a lowercase hex string.

func CertFingerprintColonSHA1

func CertFingerprintColonSHA1(cert *x509.Certificate) string

CertFingerprintColonSHA1 returns the SHA-1 fingerprint of a certificate in uppercase colon-separated hex format (AA:BB:CC:...), matching the format used by OpenSSL and browser certificate viewers.

func CertFingerprintColonSHA256

func CertFingerprintColonSHA256(cert *x509.Certificate) string

CertFingerprintColonSHA256 returns the SHA-256 fingerprint of a certificate in uppercase colon-separated hex format (AA:BB:CC:...), matching the format used by OpenSSL and browser certificate viewers.

func CertFingerprintSHA1

func CertFingerprintSHA1(cert *x509.Certificate) string

CertFingerprintSHA1 returns the SHA-1 fingerprint of a certificate as a lowercase hex string. SHA-1 fingerprints are widely used in browser UIs, CT logs, and legacy systems.

func CertSKI added in v0.2.1

func CertSKI(cert *x509.Certificate) string

CertSKI computes a Subject Key Identifier from the certificate's public key per RFC 7093 Section 2 Method 1: the leftmost 160 bits of the SHA-256 hash of the BIT STRING value of subjectPublicKey (excluding tag, length, and unused-bits octet). The result is 20 bytes, the same length as a SHA-1 SKI, ensuring compatibility.

func CertSKIEmbedded added in v0.2.1

func CertSKIEmbedded(cert *x509.Certificate) string

CertSKIEmbedded returns the Subject Key Identifier as stored in the certificate extension, as a colon-separated hex string. This may be SHA-1 (20 bytes) or SHA-256 (32 bytes) depending on the issuing CA. Returns empty string if the extension is not present.

func CertToPEM

func CertToPEM(cert *x509.Certificate) string

CertToPEM encodes a certificate as PEM.

func ClassifyHosts

func ClassifyHosts(hosts []string) (dnsNames []string, ips []net.IP, uris []*url.URL, emails []string)

ClassifyHosts splits a mixed host list into DNS names, IPs, URIs, and emails. Classification order: net.ParseIP for IPs, contains "://" for URIs, contains "@" for emails, otherwise DNS name.

func ColonHex

func ColonHex(b []byte) string

ColonHex formats a byte slice as colon-separated lowercase hex.

func ComputeSKI added in v0.2.1

func ComputeSKI(pub crypto.PublicKey) ([]byte, error)

ComputeSKI computes a Subject Key Identifier using RFC 7093 Method 1: SHA-256 of subjectPublicKey BIT STRING bytes, truncated to 160 bits (20 bytes).

func ComputeSKILegacy added in v0.2.1

func ComputeSKILegacy(pub crypto.PublicKey) ([]byte, error)

ComputeSKILegacy computes a Subject Key Identifier using the RFC 5280 method: SHA-1 of subjectPublicKey BIT STRING bytes (20 bytes). Used only for AKI cross-matching with legacy certificates.

func DecodeJKS

func DecodeJKS(data []byte, passwords []string) ([]*x509.Certificate, []crypto.PrivateKey, error)

DecodeJKS decodes a Java KeyStore (JKS) and returns the certificates and private keys it contains. Passwords are tried in order to open the store. For private key entries, all passwords are tried independently since the key password may differ from the store password.

TrustedCertificateEntry entries yield certificates. PrivateKeyEntry entries yield PKCS#8 private keys and their certificate chains. Individual entry errors are skipped; an error is returned only if the store cannot be loaded or no usable entries are found.

func DecodePKCS7

func DecodePKCS7(derData []byte) ([]*x509.Certificate, error)

DecodePKCS7 decodes a DER-encoded PKCS#7 bundle and returns the certificates it contains. Returns an error if decoding fails or the bundle contains no certificates.

func DecodePKCS12

func DecodePKCS12(pfxData []byte, password string) (crypto.PrivateKey, *x509.Certificate, []*x509.Certificate, error)

DecodePKCS12 decodes a PKCS#12/PFX bundle and returns the private key, leaf certificate, and CA certificates. Returns an error if decoding fails.

func DefaultPasswords

func DefaultPasswords() []string

DefaultPasswords returns the list of passwords tried by default when decrypting password-protected PEM blocks or PKCS#12 files. Returns a fresh copy each call.

func EncodeJKS

func EncodeJKS(privateKey crypto.PrivateKey, leaf *x509.Certificate, caCerts []*x509.Certificate, password string) ([]byte, error)

EncodeJKS creates a Java KeyStore (JKS) containing a private key entry with its certificate chain. The leaf certificate and intermediates form the chain stored under the alias "server". The same password protects both the store and the key entry (standard Java convention).

func EncodePKCS7

func EncodePKCS7(certs []*x509.Certificate) ([]byte, error)

EncodePKCS7 creates a certs-only PKCS#7/P7B bundle from a certificate chain. Returns the DER-encoded PKCS#7 SignedData structure.

func EncodePKCS12

func EncodePKCS12(privateKey crypto.PrivateKey, leaf *x509.Certificate, caCerts []*x509.Certificate, password string) ([]byte, error)

EncodePKCS12 creates a PKCS#12/PFX bundle from a private key, leaf cert, CA chain, and password. Returns the DER-encoded PKCS#12 data.

func EncodePKCS12Legacy

func EncodePKCS12Legacy(privateKey crypto.PrivateKey, leaf *x509.Certificate, caCerts []*x509.Certificate, password string) ([]byte, error)

EncodePKCS12Legacy creates a PKCS#12/PFX bundle using the legacy RC2 cipher for compatibility with older Java keystores. Returns the DER-encoded PKCS#12 data.

func FetchAIACertificates

func FetchAIACertificates(ctx context.Context, cert *x509.Certificate, timeout time.Duration, maxDepth int) ([]*x509.Certificate, []string)

FetchAIACertificates follows AIA CA Issuers URLs to fetch intermediate certificates.

func FetchLeafFromURL

func FetchLeafFromURL(ctx context.Context, rawURL string, timeout time.Duration) (*x509.Certificate, error)

FetchLeafFromURL connects to the given HTTPS URL via TLS and returns the leaf (server) certificate from the handshake.

func GenerateCSR

func GenerateCSR(leaf *x509.Certificate, privateKey crypto.PrivateKey) (csrPEM string, keyPEM string, err error)

GenerateCSR creates a Certificate Signing Request that copies Subject, DNSNames, IPAddresses, and URIs from the given leaf certificate. If privateKey is nil, a new EC P-256 key is generated. Returns the PEM-encoded CSR and, if a key was auto-generated, its PEM-encoded PKCS#8 private key (empty string if caller provided the key).

func GenerateCSRFromCSR

func GenerateCSRFromCSR(source *x509.CertificateRequest, signer crypto.Signer) (string, error)

GenerateCSRFromCSR creates a new CSR using an existing CSR as template, signed by the provided key.

func GenerateCSRFromTemplate

func GenerateCSRFromTemplate(tmpl *CSRTemplate, signer crypto.Signer) (string, error)

GenerateCSRFromTemplate creates a PEM-encoded CSR from a template and signer.

func GenerateECKey

func GenerateECKey(curve elliptic.Curve) (*ecdsa.PrivateKey, error)

GenerateECKey generates a new ECDSA private key on the given curve.

func GenerateEd25519Key

func GenerateEd25519Key() (ed25519.PublicKey, ed25519.PrivateKey, error)

GenerateEd25519Key generates a new Ed25519 key pair.

func GenerateRSAKey

func GenerateRSAKey(bits int) (*rsa.PrivateKey, error)

GenerateRSAKey generates a new RSA private key with the given bit size.

func GetCertificateType

func GetCertificateType(cert *x509.Certificate) string

GetCertificateType determines if a certificate is root, intermediate, or leaf.

func GetPublicKey

func GetPublicKey(priv crypto.PrivateKey) (crypto.PublicKey, error)

GetPublicKey extracts the public key from a private key via crypto.Signer.

func IsPEM

func IsPEM(data []byte) bool

IsPEM returns true if the data appears to contain PEM-encoded content.

func KeyAlgorithmName

func KeyAlgorithmName(key crypto.PrivateKey) string

KeyAlgorithmName returns a human-readable name for a private key's algorithm.

func KeyMatchesCert

func KeyMatchesCert(priv crypto.PrivateKey, cert *x509.Certificate) (bool, error)

KeyMatchesCert reports whether a private key corresponds to the public key in a certificate. Uses the Equal method available on all standard public key types since Go 1.20, which handles cross-type mismatches by returning false.

func MarshalPrivateKeyToPEM

func MarshalPrivateKeyToPEM(key crypto.PrivateKey) (string, error)

MarshalPrivateKeyToPEM marshals a private key to PKCS#8 PEM format. Supports ECDSA, RSA, and Ed25519 keys.

func MarshalPublicKeyToPEM

func MarshalPublicKeyToPEM(pub crypto.PublicKey) (string, error)

MarshalPublicKeyToPEM marshals a public key to PKIX PEM format. Supports RSA, ECDSA, and Ed25519 public keys.

func ParsePEMCertificate

func ParsePEMCertificate(pemData []byte) (*x509.Certificate, error)

ParsePEMCertificate parses a single certificate from PEM data.

func ParsePEMCertificateRequest

func ParsePEMCertificateRequest(pemData []byte) (*x509.CertificateRequest, error)

ParsePEMCertificateRequest parses a single certificate request from PEM data.

func ParsePEMCertificates

func ParsePEMCertificates(pemData []byte) ([]*x509.Certificate, error)

ParsePEMCertificates parses all certificates from a PEM bundle.

func ParsePEMPrivateKey

func ParsePEMPrivateKey(pemData []byte) (crypto.PrivateKey, error)

ParsePEMPrivateKey parses a PEM-encoded private key (PKCS#1, PKCS#8, or EC). For "PRIVATE KEY" blocks it tries PKCS#8 first, then falls back to PKCS#1 and EC parsers to handle mislabeled keys (e.g., from pkcs12.ToPEM).

func ParsePEMPrivateKeyWithPasswords

func ParsePEMPrivateKeyWithPasswords(pemData []byte, passwords []string) (crypto.PrivateKey, error)

ParsePEMPrivateKeyWithPasswords tries to parse a PEM-encoded private key. It first attempts unencrypted parsing via ParsePEMPrivateKey. If that fails and the PEM block is encrypted (legacy RFC 1423), it tries each password in order. Returns the first successfully decrypted key, or an error if all passwords fail.

func PublicKeyAlgorithmName

func PublicKeyAlgorithmName(key crypto.PublicKey) string

PublicKeyAlgorithmName returns a human-readable name for a public key's algorithm.

func VerifyCSR

func VerifyCSR(csr *x509.CertificateRequest) error

VerifyCSR checks that the signature on a certificate signing request is valid.

Types

type BundleOptions

type BundleOptions struct {
	// ExtraIntermediates are additional intermediates to consider during chain building.
	ExtraIntermediates []*x509.Certificate
	// FetchAIA enables fetching intermediate certificates via AIA CA Issuers URLs.
	FetchAIA bool
	// AIATimeout is the HTTP timeout for AIA fetches.
	AIATimeout time.Duration
	// AIAMaxDepth is the maximum number of AIA hops to follow.
	AIAMaxDepth int
	// TrustStore selects the root certificate pool: "system", "mozilla", or "custom".
	TrustStore string
	// CustomRoots are root certificates used when TrustStore is "custom".
	CustomRoots []*x509.Certificate
	// Verify enables chain verification against the trust store.
	Verify bool
	// IncludeRoot includes the root certificate in the result.
	IncludeRoot bool
}

BundleOptions configures chain resolution.

func DefaultOptions

func DefaultOptions() BundleOptions

DefaultOptions returns sensible defaults.

type BundleResult

type BundleResult struct {
	// Leaf is the end-entity certificate.
	Leaf *x509.Certificate
	// Intermediates are the CA certificates between the leaf and root.
	Intermediates []*x509.Certificate
	// Roots are the trust anchor certificates (typically one).
	Roots []*x509.Certificate
	// Warnings are non-fatal issues found during chain resolution.
	Warnings []string
}

BundleResult holds the resolved chain and metadata.

func Bundle

func Bundle(ctx context.Context, leaf *x509.Certificate, opts BundleOptions) (*BundleResult, error)

Bundle resolves the full certificate chain for a leaf certificate.

type CSRSubject

type CSRSubject struct {
	CommonName         string   `json:"common_name"`
	Organization       []string `json:"organization,omitempty"`
	OrganizationalUnit []string `json:"organizational_unit,omitempty"`
	Country            []string `json:"country,omitempty"`
	Province           []string `json:"province,omitempty"`
	Locality           []string `json:"locality,omitempty"`
}

CSRSubject holds the subject fields for a CSR template.

type CSRTemplate

type CSRTemplate struct {
	// Subject contains the distinguished name fields for the CSR.
	Subject CSRSubject `json:"subject"`
	// Hosts lists the DNS names, IP addresses, URIs, and email addresses for SANs.
	Hosts []string `json:"hosts"`
}

CSRTemplate is a JSON-serializable template for CSR generation.

func ParseCSRTemplate

func ParseCSRTemplate(data []byte) (*CSRTemplate, error)

ParseCSRTemplate unmarshals JSON data into a CSRTemplate.

Directories

Path Synopsis
cmd
certkit command
wasm command
Package main implements a WASM build of certkit for browser-based certificate processing.
Package main implements a WASM build of certkit for browser-based certificate processing.

Jump to

Keyboard shortcuts

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