x509-crystal v0.1.4

Crystal bindings to Go's crypto/x509 via a shared library

x509-crystal

Pure Crystal X.509 certificate generation.

Generates self-signed and CA-signed X.509 certificates for mTLS and PKI use cases, with support for ECDSA P-256 and RSA at user-specified key sizes.

Why

Crystal's standard library ships with OpenSSL bindings for TLS, but doesn't expose the certificate generation surface. This shard adds the missing pieces via direct LibCrypto bindings — no Go toolchain, no CGo, no shared library to manage.

Requirements

  • Crystal >= 1.20.0

Installation

Add to your shard.yml:

dependencies:
  x509-crystal:
    github: dirless/x509-crystal

Run shards install.

Usage

require "x509-crystal"

# Self-signed (testing / development)
bundle = X509.generate(common_name: "tenant-abc123", days: 3650)

bundle.ca_cert     # String (PEM) — self-signed CA certificate
bundle.ca_key      # String (PEM) — CA private key (PKCS8)
bundle.client_cert # String (PEM) — client cert signed by the CA
bundle.client_key  # String (PEM) — client private key (PKCS8)

# CA-signed (production — bring your own CA)
bundle = X509.generate(
  common_name: "tenant-abc123",
  days:        3650,
  ca_cert:     File.read("my-ca.crt"),
  ca_key:      File.read("my-ca.key"),
)
# bundle.ca_cert / bundle.ca_key echo back the values you provided
# bundle.client_cert / bundle.client_key are freshly generated

# RSA instead of the default ECDSA
bundle = X509.generate(
  common_name:      "tenant-abc123",
  days:             3650,
  ca_algorithm:     X509::Algorithm::RSA,
  client_algorithm: X509::Algorithm::RSA,
  ca_rsa_bits:      4096,
  client_rsa_bits:  4096,
)

# Mixed algorithms — ECDSA CA with RSA client cert (or vice versa)
bundle = X509.generate(
  common_name:      "tenant-abc123",
  days:             3650,
  ca_algorithm:     X509::Algorithm::ECDSA,
  client_algorithm: X509::Algorithm::RSA,
  client_rsa_bits:  2048,
)

API

X509.generate

X509.generate(
  common_name      : String,
  days             : Int32,
  ca_algorithm     : X509::Algorithm = X509::Algorithm::ECDSA,
  client_algorithm : X509::Algorithm? = nil,    # defaults to ca_algorithm
  ca_rsa_bits      : Int32 = 4096,
  client_rsa_bits  : Int32 = 4096,
  ca_cert          : String? = nil,             # omit for self-signed mode
  ca_key           : String? = nil,
) : X509::CertBundle

Raises X509::Error on failure.

days applies to both the CA and client cert in self-signed mode. In CA-signed mode it only affects the client cert — the provided CA's validity is already fixed.

client_algorithm defaults to ca_algorithm when not set, so both certs use the same algorithm unless you explicitly override it.

X509::Algorithm

X509::Algorithm::ECDSA  # P-256 (default)
X509::Algorithm::RSA    # RSA with user-specified bit size

X509::CertBundle

bundle.ca_cert     : String  # PEM-encoded CA certificate
bundle.ca_key      : String  # PEM-encoded CA private key (PKCS8)
bundle.client_cert : String  # PEM-encoded client certificate
bundle.client_key  : String  # PEM-encoded client private key (PKCS8)

Error handling

All errors raise X509::Error with a descriptive message. Common causes:

  • Empty common_name
  • Non-positive days
  • RSA *_rsa_bits below 2048
  • Invalid or non-CA cert provided as ca_cert
  • Mismatched ca_cert and ca_key
  • Only one of ca_cert / ca_key provided (both or neither)

Inspecting generated certificates

require "x509-crystal"

bundle = X509.generate(common_name: "tenant-abc123", days: 3650)

File.write("ca.crt",     bundle.ca_cert)
File.write("client.crt", bundle.client_cert)
File.write("client.key", bundle.client_key)

Inspect with OpenSSL:

# CA certificate — should show CA:TRUE
$ openssl x509 -in ca.crt -noout -text | grep -E "Subject:|Issuer:|CA:|Key Usage:|Not (Before|After)"
        Subject: CN=tenant-abc123, O=x509-crystal
        Issuer: CN=tenant-abc123, O=x509-crystal
        Not Before: Apr 19 00:00:00 2026 GMT
        Not After : Apr 16 00:00:00 2036 GMT
            CA:TRUE, pathlen:1
            X509v3 Key Usage: critical

# Client certificate — should show CA:FALSE and clientAuth EKU
$ openssl x509 -in client.crt -noout -text | grep -E "Subject:|Issuer:|CA:|Key Usage:|Extended|Not (Before|After)"
        Subject: CN=tenant-abc123, O=x509-crystal
        Issuer: CN=tenant-abc123, O=x509-crystal
        Not Before: Apr 19 00:00:00 2026 GMT
        Not After : Apr 16 00:00:00 2036 GMT
            CA:FALSE
            X509v3 Key Usage: critical
            X509v3 Extended Key Usage:
                TLS Web Client Authentication

Testing

crystal spec

License

MIT

Repository

x509-crystal

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 1
  • about 3 hours ago
  • March 2, 2026
License

Links
Synced at

Sun, 19 Apr 2026 23:41:22 GMT

Languages