jsec  Overview

The jsec/ca module provides a complete Certificate Authority (CA) implementation for managing X.509 certificates. It supports:

  • Root CA generation
  • Intermediate CA generation
  • Certificate issuance (from CSRs or direct generation)
  • Certificate revocation and CRL generation
  • OCSP response creation (mechanics only - you provide HTTP server)

For API reference, see API.org - jsec/ca section.

Design Philosophy

  • Simple API for common cases - :issue generates key + cert in one call
  • Powerful API for complex cases - :sign-csr gives full control
  • Methods AND functions - Every operation works both ways
  • Stateless by default - Tracking is opt-in for memory efficiency
  • Crypto only - We handle certificates, you handle networking

Quick Start

(import jsec/ca)

# Generate a root CA
(def root-ca (ca/generate {:common-name "My Root CA"
                           :days-valid 3650}))

# Issue a server certificate
(def server (ca/issue root-ca {:common-name "server.example.com"
                               :san ["DNS:server.example.com"
                                     "DNS:localhost"
                                     "IP:127.0.0.1"]
                               :extended-key-usage "serverAuth"}))

# Use the certificate
(print (server :cert))  # PEM certificate
(print (server :key))   # PEM private key

# Get CA cert for trust stores
(print (:get-cert root-ca))

OCSP Responder Example

You implement the HTTP server; the CA module provides the OCSP mechanics:

(import jsec/ca)

# Your CA
(def ca (ca/create (slurp "ca.crt") (slurp "ca.key")))

# Your certificate status lookup (implement based on your storage)
(defn lookup-status [serial]
  (if (revoked? serial)
    :revoked
    :good))

# OCSP handler for your HTTP server
(defn handle-ocsp [request]
  (def parsed (ca/parse-ocsp-request (request :body)))
  (def status (lookup-status (parsed :serial)))
  (def response (:create-ocsp-response ca parsed status))

  {:status 200
   :headers {"Content-Type" "application/ocsp-response"}
   :body response})

Key Types

Supported key types for CA and certificate generation:

Keyword Algorithm Size/Curve Notes
:ec-p256 EC P-256 Default, fast
:ec EC P-256 Alias
:ec-p384 EC P-384 Higher security
:ec-p521 EC P-521 Maximum security
:rsa-2048 RSA 2048 bits Compatibility
:rsa RSA 2048 bits Alias
:rsa-4096 RSA 4096 bits Higher security

EC keys are recommended for new deployments. RSA keys are available for compatibility with legacy systems.

Serial Number Persistence

For production CAs, serial numbers must be persisted to avoid reuse:

(import jsec/ca)

# Save before shutdown
(defn save-ca-state [ca path]
  (spit path (string (:get-serial ca))))

# Restore on startup
(defn load-ca [cert-path key-path serial-path]
  (def cert (slurp cert-path))
  (def key (slurp key-path))
  (def serial (scan-number (slurp serial-path)))
  (ca/create cert key {:serial serial}))

Certificate Tracking

By default, CAs don't track issued certificates (memory efficient). Enable tracking when you need to:

  • List all issued certificates
  • Implement revocation by certificate (not just serial)
  • Audit certificate issuance

    Enable tracking

    (def ca (ca/generate {:track-issued true}))

    Issue some certificates

    (:issue ca {:common-name "cert1"}) (:issue ca {:common-name "cert2"})

    Get all issued certs

    (def issued (:get-issued ca)) (print (length issued)) # => 2

Subject Alternative Names (SAN)

The :san option accepts an array of SAN entries:

:san ["DNS:example.com"
      "DNS:www.example.com"
      "DNS:*.example.com"
      "IP:192.168.1.1"
      "IP:10.0.0.1"
      "email:admin@example.com"]

Prefix types:

Prefix Description
DNS: DNS name (most common)
IP: IP address
email: Email address
URI: Uniform Resource ID

PKI Hierarchy Example

(import jsec/ca)

# Root CA (offline, long-lived)
(def root (ca/generate {:common-name "Root CA"
                        :days-valid 7300     # 20 years
                        :organization "My Org"
                        :country "US"}))

# Intermediate CA (online, medium-lived)
(def intermediate (ca/generate-intermediate root
                    {:common-name "Intermediate CA"
                     :days-valid 1825        # 5 years
                     :path-length 0}))       # No sub-CAs

# Issue end-entity certificates from intermediate
(def server (:issue intermediate
              {:common-name "server.example.com"
               :san ["DNS:server.example.com"]
               :extended-key-usage "serverAuth"
               :days-valid 365}))

# Chain for server: server cert + intermediate cert
(def chain (string (server :cert) (:get-cert intermediate)))