certkit

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Feb 14, 2026 License: MIT Imports: 30 Imported by: 0

README

certkit

A certificate management tool that ingests TLS/SSL certificates and private keys in various formats, catalogs them in a SQLite database, and exports organized bundles in multiple output formats.

Features

  • Multi-format ingestion -- PEM, DER, PKCS#12, PKCS#7, JKS, PKCS#8, encrypted PEM keys
  • Automatic classification -- Identifies root, intermediate, and leaf certificates
  • Key type support -- RSA, ECDSA, and Ed25519
  • Chain bundling -- Resolves certificate chains via AIA fetching with Mozilla or system trust stores
  • Bundle export -- Generates output in PEM (leaf, chain, fullchain, intermediates, root), PKCS#12, JKS, Kubernetes Secret YAML, JSON, YAML, and CSR formats
  • Smart CSR generation -- Produces renewal CSRs from existing certificates, JSON templates, or existing CSRs with configurable subject fields and intelligent SAN filtering
  • SQLite catalog -- Persistent or in-memory database indexed by Subject Key Identifier, serial number, and Authority Key Identifier
  • Encrypted key handling -- Tries user-supplied passwords plus common defaults ("", "password", "changeit")
  • Certificate inspection -- Display detailed certificate, key, and CSR information (text or JSON)
  • Verification -- Check chain validity, key-cert matching, and expiry windows
  • Key generation -- Generate RSA, ECDSA, or Ed25519 key pairs with optional CSR

Install

Homebrew (macOS)
brew tap sensiblebit/tap
brew install 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+. No CGO required.

go build -o certkit ./cmd/certkit/

Usage

Commands
Command Description
certkit scan <path> Scan and catalog certificates and keys, optionally export bundles
certkit bundle <file> Build a certificate chain from a leaf cert, P12, JKS, or P7B
certkit inspect <file> Display certificate, key, or CSR information
certkit verify <file> Verify chain, key-cert match, or expiry
certkit keygen Generate key pairs and optionally CSRs
certkit csr Generate a CSR from a template, certificate, or existing CSR
Global Flags
Flag Default Description
--log-level, -l info Log level: debug, info, warn, error
--passwords, -p (empty) Comma-separated passwords for encrypted keys
--password-file (empty) File containing passwords, one per line
Scan Flags
Flag Default Description
--db, -d (empty) SQLite database path (empty = in-memory)
--bundle false Export certificate bundles after scanning
--config, -c ./bundles.yaml Path to bundle config YAML
--out-path, -o (required) Output directory for exported bundles
--force, -f false Allow export of untrusted certificate bundles
--duplicates false Export all certificates per bundle, not just the newest
--dump-keys (empty) Dump all discovered keys to a single PEM file
--dump-certs (empty) Dump all discovered certificates to a single PEM file
--max-file-size 10485760 Skip files larger than this size in bytes (0 to disable)
Bundle Flags
Flag Default Description
--key (empty) 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
Verify Flags
Flag Default Description
--key (empty) Private key file to check against the certificate
--expiry, -e (empty) Check if cert expires within duration (e.g., 30d, 720h)
--trust-store mozilla Trust store for chain validation: system, mozilla

Accepts PEM, DER, PKCS#12, JKS, or PKCS#7 input. The chain is always verified. When the input contains an embedded private key (PKCS#12, JKS), the key match is checked automatically.

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 (omit to print PEM to stdout)
--cn (empty) Common Name (triggers CSR generation)
--sans (empty) Comma-separated SANs (triggers CSR generation)
CSR Flags
Flag Default Description
--template (empty) JSON template file for CSR generation
--cert (empty) PEM certificate to use as CSR template
--from-csr (empty) Existing PEM CSR to re-sign with a new key
--key (empty) 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 (omit to print PEM to stdout)

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

Examples

Scan a directory and export bundles:

certkit scan ./certs/ --bundle

Scan and export with a persistent database:

certkit scan ./certs/ --bundle -d certs.db -o ./bundles

Scan only (no export):

certkit scan ./certs/ -d certs.db

Build a chain bundle from a leaf cert (outputs PEM to stdout):

certkit bundle leaf.pem

Build a chain with a key, write to file:

certkit bundle leaf.pem --key key.pem -o chain.pem

Bundle a PKCS#12 file to PEM:

certkit bundle server.p12 -p "secret"

Build a fullchain (includes root):

certkit bundle leaf.pem --format fullchain -o fullchain.pem

Export as PKCS#12:

certkit bundle leaf.pem --key key.pem --format p12 -o bundle.p12

Dump all discovered keys and certificates:

certkit scan ./certs/ --dump-keys all-keys.pem --dump-certs all-certs.pem

Read from stdin:

cat server.pem | certkit scan -

Inspect a certificate file:

certkit inspect cert.pem
certkit inspect cert.pem --format json

Verify a certificate (chain is always checked, key match is automatic for containers):

certkit verify cert.pem
certkit verify store.p12
certkit verify cert.pem --key key.pem --expiry 30d

Generate an ECDSA key pair (prints PEM to stdout):

certkit keygen

Generate an RSA key pair and write to files:

certkit keygen -a rsa -b 4096 -o ./keys

Generate a key pair with a CSR:

certkit keygen --cn example.com --sans "example.com,www.example.com"

Generate a CSR from a JSON template (prints PEM to stdout):

certkit csr --template request.json

Generate a CSR from an existing certificate:

certkit csr --cert existing.pem --algorithm rsa --bits 4096

Re-sign an existing CSR with a new key:

certkit csr --from-csr old.csr --key mykey.pem

Provide passwords for encrypted PKCS#12 or PEM files:

certkit scan ./certs/ -p "secret1,secret2" --password-file extra_passwords.txt

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

Output Files

When running certkit scan --bundle, each bundle produces the following files under <out>/<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). When multiple certificates match the same bundle, the newest gets the bare name and older ones receive a _<date>_<serial> suffix.

How It Works

Input files/stdin
       |
       v
  Format detection (PEM vs DER)
       |
       v
  Parse certificates, keys, CSRs
  (handles PKCS#12, PKCS#7, JKS, encrypted PEM, PKCS#8, SEC1, Ed25519)
       |
       v
  Store in SQLite (certificates + keys indexed by SKI)
       |
       v
  Resolve AKIs (match legacy SHA-1 AKIs to computed RFC 7093 M1 SKIs)
       |
       v
  [if --bundle] Match keys to certs, build chains via certkit.Bundle,
  write all output formats per bundle

Expired certificates are skipped during ingestion. Root certificates use their own Subject Key Identifier as their Authority Key Identifier (self-signed). Non-root certificate AKIs are resolved post-ingestion by matching embedded AKIs against a multi-hash lookup (RFC 7093 M1 SHA-256 + legacy SHA-1) of all CA certificates.

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
opts := certkit.DefaultOptions()
opts.TrustStore = "mozilla"
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")

License

MIT

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

Jump to

Keyboard shortcuts

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