CrackedRuby CrackedRuby

Overview

Digital certificates serve as the foundation of public key infrastructure (PKI), enabling secure communication across networks. A digital certificate combines a public key with identifying information about its owner, digitally signed by a certificate authority (CA) to verify authenticity. This mechanism solves the key distribution problem in public key cryptography - how to securely share public keys while ensuring they belong to the claimed entity.

The X.509 standard defines the most widely used certificate format. These certificates contain the subject's public key, identifying information (such as domain name or organization), validity period, and the CA's digital signature. When a client connects to a server using TLS/SSL, the server presents its certificate. The client validates the certificate by checking the CA's signature, confirming the certificate hasn't expired, and verifying the hostname matches the certificate's subject.

require 'openssl'
require 'net/http'

# Fetch and examine a certificate
uri = URI('https://www.example.com')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

http.start do |connection|
  cert = connection.peer_cert
  puts "Subject: #{cert.subject}"
  puts "Issuer: #{cert.issuer}"
  puts "Valid from: #{cert.not_before}"
  puts "Valid until: #{cert.not_after}"
  puts "Serial: #{cert.serial}"
end

Digital certificates power HTTPS websites, email encryption (S/MIME), code signing, client authentication, and VPNs. The trust model relies on certificate authorities - organizations trusted to verify identities before issuing certificates. Browsers and operating systems maintain lists of trusted root CAs. Any certificate signed by these roots, or by intermediate CAs authorized by roots, inherits that trust.

Key Principles

Digital certificates operate on asymmetric cryptography principles. Each entity possesses a key pair: a private key kept secret and a public key distributed freely. The certificate binds the public key to an identity, preventing impersonation attacks. The mathematical relationship between the keys ensures data encrypted with one key can only be decrypted with its pair.

The X.509 certificate structure contains multiple fields with specific purposes. The subject identifies the certificate holder using distinguished names (DN) with components like common name (CN), organization (O), and country (C). The issuer identifies the CA that signed the certificate. The validity period defines the timeframe during which the certificate should be trusted. The public key contains the actual cryptographic key and algorithm information. The signature proves the CA verified the subject's identity and approved the certificate.

Certificate chains establish trust hierarchically. A root CA certificate is self-signed - its issuer and subject are identical. Root CAs issue intermediate CA certificates, which in turn sign end-entity certificates. This chain structure allows root CAs to remain offline for security while intermediate CAs handle day-to-day operations. Validation requires checking each certificate in the chain back to a trusted root.

require 'openssl'

# Parse a certificate and examine its structure
cert_pem = File.read('certificate.pem')
cert = OpenSSL::X509::Certificate.new(cert_pem)

# Extract subject components
subject = cert.subject.to_a
subject.each do |component|
  key, value, type = component
  puts "#{key}: #{value}"
end

# Display extensions
cert.extensions.each do |ext|
  puts "#{ext.oid}: #{ext.value}"
end

Certificate extensions add functionality beyond basic identification. The Subject Alternative Name (SAN) extension lists additional identities the certificate covers, such as multiple domain names. The Key Usage extension specifies permitted cryptographic operations like digital signatures or key encryption. The Basic Constraints extension indicates whether the certificate can sign other certificates, distinguishing CA certificates from end-entity certificates.

The validation process involves multiple checks. First, verify the signature chain from the end-entity certificate to a trusted root. Each certificate's signature must validate against its issuer's public key. Second, check each certificate's validity period against the current time. Third, verify the end-entity certificate's subject matches the expected identity (hostname for TLS). Fourth, check revocation status - whether any certificate in the chain has been revoked before expiration.

Certificate revocation addresses compromised or incorrectly issued certificates. Two primary mechanisms exist: Certificate Revocation Lists (CRL) and Online Certificate Status Protocol (OCSP). CRLs are periodically published lists of revoked certificate serial numbers. OCSP provides real-time revocation checking by querying a responder. Modern browsers increasingly use OCSP stapling, where servers attach signed OCSP responses to TLS handshakes, improving performance and privacy.

Ruby Implementation

Ruby's OpenSSL library provides comprehensive certificate handling through the OpenSSL::X509 module. The library wraps the OpenSSL C library, exposing certificate creation, parsing, validation, and chain building functionality.

Creating a self-signed certificate requires generating a key pair, instantiating a certificate object, setting its fields, and signing it with the private key:

require 'openssl'

# Generate a 2048-bit RSA key pair
key = OpenSSL::PKey::RSA.new(2048)

# Create a certificate
cert = OpenSSL::X509::Certificate.new
cert.version = 2  # X.509 v3
cert.serial = 1
cert.subject = OpenSSL::X509::Name.parse("/CN=localhost/O=Development/C=US")
cert.issuer = cert.subject  # Self-signed
cert.public_key = key.public_key
cert.not_before = Time.now
cert.not_after = Time.now + (365 * 24 * 60 * 60)  # 1 year validity

# Add extensions
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = cert

cert.add_extension(ef.create_extension("basicConstraints", "CA:TRUE", true))
cert.add_extension(ef.create_extension("keyUsage", "keyCertSign, cRLSign", true))
cert.add_extension(ef.create_extension("subjectKeyIdentifier", "hash", false))

# Sign the certificate
cert.sign(key, OpenSSL::Digest::SHA256.new)

# Save to files
File.write('certificate.pem', cert.to_pem)
File.write('private_key.pem', key.to_pem)

Reading and parsing existing certificates converts PEM or DER encoded data into certificate objects. Ruby handles both formats transparently:

# Read from PEM file
cert_pem = File.read('certificate.pem')
cert = OpenSSL::X509::Certificate.new(cert_pem)

# Read from DER binary
cert_der = File.binread('certificate.der')
cert = OpenSSL::X509::Certificate.new(cert_der)

# Extract specific fields
puts "Serial Number: #{cert.serial.to_i}"
puts "Signature Algorithm: #{cert.signature_algorithm}"

# Check if certificate is a CA
basic_constraints = cert.extensions.find { |ext| ext.oid == "basicConstraints" }
is_ca = basic_constraints && basic_constraints.value.include?("CA:TRUE")
puts "Is CA: #{is_ca}"

Certificate validation in Ruby requires building and verifying the certificate chain. The OpenSSL::X509::Store class manages trusted certificates and performs validation:

require 'openssl'

def validate_certificate(cert_path, ca_cert_path)
  cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
  ca_cert = OpenSSL::X509::Certificate.new(File.read(ca_cert_path))
  
  # Create a certificate store
  store = OpenSSL::X509::Store.new
  store.add_cert(ca_cert)
  
  # Verify the certificate
  if store.verify(cert)
    puts "Certificate is valid"
    true
  else
    puts "Certificate validation failed: #{store.error_string}"
    false
  end
end

# Load system default certificates
store = OpenSSL::X509::Store.new
store.set_default_paths

# Add custom CA certificates
custom_ca = OpenSSL::X509::Certificate.new(File.read('custom_ca.pem'))
store.add_cert(custom_ca)

# Verify a certificate chain
cert = OpenSSL::X509::Certificate.new(File.read('server_cert.pem'))
result = store.verify(cert)
puts "Verification result: #{result}"

Ruby's HTTP libraries integrate certificate validation automatically. Configuring SSL/TLS options controls certificate verification behavior:

require 'net/http'
require 'uri'

uri = URI('https://secure.example.com')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

# Default: verify certificates against system CA store
http.verify_mode = OpenSSL::SSL::VERIFY_PEER

# Use custom CA certificate
http.ca_file = '/path/to/ca_bundle.crt'

# Or load from directory
http.ca_path = '/etc/ssl/certs'

# Disable verification (dangerous - only for development)
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

# Verify certificate hostname
http.verify_hostname = true

response = http.get(uri.path)

The OpenSSL library also supports certificate signing requests (CSR), which entities submit to CAs for certificate issuance:

# Generate CSR
key = OpenSSL::PKey::RSA.new(2048)
csr = OpenSSL::X509::Request.new
csr.version = 0
csr.subject = OpenSSL::X509::Name.parse("/CN=www.example.com/O=Example Corp/C=US")
csr.public_key = key.public_key

# Add extensions to CSR
extensions = OpenSSL::ASN1::Set([
  OpenSSL::ASN1::Sequence([
    OpenSSL::X509::Extension.new("subjectAltName", 
      "DNS:www.example.com,DNS:example.com")
  ])
])
csr.add_attribute(OpenSSL::X509::Attribute.new("extReq", extensions))

# Sign the CSR
csr.sign(key, OpenSSL::Digest::SHA256.new)

File.write('request.csr', csr.to_pem)

Security Implications

Certificate security depends on protecting private keys and validating certificates correctly. Compromised private keys allow attackers to impersonate certificate owners, decrypt encrypted communications, and sign malicious content. Private keys must never be transmitted over networks, committed to version control, or stored without encryption.

The certificate trust model assumes CA integrity. Compromised or malicious CAs can issue fraudulent certificates for any domain. Several incidents have exposed CA vulnerabilities, leading to the development of Certificate Transparency (CT) - a public log system where CAs must record issued certificates. Browsers reject certificates not recorded in CT logs, making rogue certificates detectable.

Man-in-the-middle (MITM) attacks exploit certificate validation failures. An attacker intercepts traffic and presents their own certificate. If the client doesn't properly validate the certificate chain, hostname, and expiration, the attack succeeds. Applications must enforce strict certificate validation and never disable checks in production environments.

# Secure certificate validation
require 'net/http'

def secure_request(url)
  uri = URI(url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  
  # Enforce peer verification
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
  
  # Verify hostname matches certificate
  http.verify_hostname = true
  
  # Use system CA certificates
  http.cert_store = OpenSSL::X509::Store.new
  http.cert_store.set_default_paths
  
  # Optional: certificate pinning for known hosts
  http.verify_callback = proc do |verify_ok, store_context|
    if verify_ok
      cert = store_context.current_cert
      # Verify certificate fingerprint matches expected value
      expected_fingerprint = "A1:B2:C3:..." # Known good fingerprint
      actual_fingerprint = Digest::SHA256.hexdigest(cert.to_der)
      verify_ok = actual_fingerprint == expected_fingerprint
    end
    verify_ok
  end
  
  http.get(uri.path)
end

Certificate pinning provides additional security by validating that a server's certificate matches a known, expected certificate or public key. This prevents attacks even if a CA is compromised. However, pinning complicates certificate rotation and can cause outages if certificates change unexpectedly.

Expired certificates present security risks. After expiration, the CA no longer guarantees the certificate's validity. Continued use allows attackers who compromised the private key after expiration to impersonate the certificate owner. Automated certificate renewal systems like Let's Encrypt address this by issuing short-lived certificates with automated renewal.

Self-signed certificates bypass the CA trust model. While acceptable for internal development, self-signed certificates should never be used in production for public-facing services. They provide encryption but no authentication - clients cannot verify the certificate owner's identity. Users must manually trust self-signed certificates, which conditions them to ignore security warnings.

Certificate revocation mechanisms have implementation challenges. CRL distribution is slow and bandwidth-intensive. OCSP adds latency to each connection and reveals browsing patterns to the CA. OCSP stapling improves privacy but requires server support. Many clients implement soft-fail policies where revocation check failures don't prevent connections, reducing security.

# Check certificate revocation status
require 'openssl'
require 'net/http'

def check_ocsp_status(cert, issuer_cert)
  # Extract OCSP responder URL from certificate
  authority_info = cert.extensions.find { |e| e.oid == "authorityInfoAccess" }
  return nil unless authority_info
  
  ocsp_url = authority_info.value.match(/OCSP - URI:(.*)/)[1]
  
  # Create OCSP request
  cert_id = OpenSSL::OCSP::CertificateId.new(cert, issuer_cert)
  request = OpenSSL::OCSP::Request.new
  request.add_certid(cert_id)
  
  # Send request to OCSP responder
  uri = URI(ocsp_url)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == 'https')
  
  response = http.post(uri.path, request.to_der, 
    'Content-Type' => 'application/ocsp-request')
  
  # Parse response
  ocsp_response = OpenSSL::OCSP::Response.new(response.body)
  basic_response = ocsp_response.basic
  
  # Verify response signature
  store = OpenSSL::X509::Store.new
  store.add_cert(issuer_cert)
  
  if basic_response.verify([], store)
    status = basic_response.status.first
    case status[1]
    when OpenSSL::OCSP::V_CERTSTATUS_GOOD
      :good
    when OpenSSL::OCSP::V_CERTSTATUS_REVOKED
      :revoked
    when OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN
      :unknown
    end
  else
    :invalid_response
  end
end

Practical Examples

Web servers use certificates to establish HTTPS connections. Configuring a Ruby web server requires loading the certificate and private key:

require 'webrick'
require 'webrick/https'

# Load certificate and key
cert = OpenSSL::X509::Certificate.new(File.read('server_cert.pem'))
key = OpenSSL::PKey::RSA.new(File.read('server_key.pem'))

# Create HTTPS server
server = WEBrick::HTTPServer.new(
  Port: 8443,
  SSLEnable: true,
  SSLCertificate: cert,
  SSLPrivateKey: key,
  SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE
)

server.mount_proc('/') do |req, res|
  res.body = 'Secure connection established'
end

trap('INT') { server.shutdown }
server.start

Client certificate authentication requires servers to verify client-presented certificates. This provides mutual authentication where both parties prove their identities:

# Server with client certificate verification
server = WEBrick::HTTPServer.new(
  Port: 8443,
  SSLEnable: true,
  SSLCertificate: OpenSSL::X509::Certificate.new(File.read('server_cert.pem')),
  SSLPrivateKey: OpenSSL::PKey::RSA.new(File.read('server_key.pem')),
  SSLVerifyClient: OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT,
  SSLCACertificateFile: 'client_ca.pem'
)

server.mount_proc('/') do |req, res|
  client_cert = req.client_cert
  if client_cert
    res.body = "Authenticated as: #{client_cert.subject}"
  else
    res.status = 401
    res.body = 'Client certificate required'
  end
end

# Client presenting certificate
uri = URI('https://localhost:8443/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.cert = OpenSSL::X509::Certificate.new(File.read('client_cert.pem'))
http.key = OpenSSL::PKey::RSA.new(File.read('client_key.pem'))
http.ca_file = 'server_ca.pem'
http.verify_mode = OpenSSL::SSL::VERIFY_PEER

response = http.get(uri.path)
puts response.body

Email encryption with S/MIME uses certificates to encrypt messages and verify sender identities. The sender encrypts using the recipient's public key from their certificate:

require 'openssl'
require 'mail'

# Load certificates and keys
sender_cert = OpenSSL::X509::Certificate.new(File.read('sender_cert.pem'))
sender_key = OpenSSL::PKey::RSA.new(File.read('sender_key.pem'))
recipient_cert = OpenSSL::X509::Certificate.new(File.read('recipient_cert.pem'))

# Create and sign email
mail = Mail.new do
  from 'sender@example.com'
  to 'recipient@example.com'
  subject 'Encrypted Message'
  body 'Confidential information'
end

# Sign the message
signed = OpenSSL::PKCS7.sign(
  sender_cert, 
  sender_key, 
  mail.to_s,
  [],
  OpenSSL::PKCS7::DETACHED
)

# Encrypt for recipient
encrypted = OpenSSL::PKCS7.encrypt(
  [recipient_cert],
  signed.to_pem,
  OpenSSL::Cipher.new('AES-256-CBC'),
  OpenSSL::PKCS7::BINARY
)

# Send encrypted message
secure_mail = Mail.new
secure_mail.from = 'sender@example.com'
secure_mail.to = 'recipient@example.com'
secure_mail.content_type = 'application/pkcs7-mime; smime-type=enveloped-data'
secure_mail.body = encrypted.to_der
secure_mail.deliver

Automated certificate provisioning with Let's Encrypt uses the ACME protocol to obtain free certificates. The acme-client gem implements this protocol:

require 'acme-client'
require 'fileutils'

# Initialize ACME client
private_key = OpenSSL::PKey::RSA.new(4096)
client = Acme::Client.new(
  private_key: private_key,
  directory: 'https://acme-v02.api.letsencrypt.org/directory'
)

# Register account
account = client.new_account(
  contact: 'mailto:admin@example.com',
  terms_of_service_agreed: true
)

# Request certificate for domain
order = client.new_order(identifiers: ['www.example.com'])

# Complete HTTP-01 challenge
authorization = order.authorizations.first
challenge = authorization.http
filename = File.join('.well-known', 'acme-challenge', challenge.token)

FileUtils.mkdir_p(File.dirname(filename))
File.write(filename, challenge.file_content)

# Trigger validation
challenge.request_validation
sleep(2) while challenge.reload.status == 'pending'

if challenge.status == 'valid'
  # Generate CSR
  csr_key = OpenSSL::PKey::RSA.new(2048)
  csr = Acme::Client::CertificateRequest.new(
    private_key: csr_key,
    subject: { common_name: 'www.example.com' }
  )
  
  # Finalize order
  order.finalize(csr: csr)
  sleep(1) while order.reload.status == 'processing'
  
  # Download certificate
  certificate = order.certificate
  File.write('certificate.pem', certificate)
  File.write('private_key.pem', csr_key.to_pem)
  
  puts "Certificate obtained successfully"
else
  puts "Challenge failed: #{challenge.error}"
end

Common Pitfalls

Certificate expiration causes service outages when certificates expire without renewal. Certificates typically have validity periods of 90 days (Let's Encrypt) to 1 year. Organizations must implement monitoring and automated renewal processes:

require 'openssl'
require 'net/smtp'

def check_certificate_expiration(cert_path, warning_days: 30)
  cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
  days_until_expiry = ((cert.not_after - Time.now) / 86400).to_i
  
  if days_until_expiry < 0
    status = :expired
    message = "Certificate expired #{days_until_expiry.abs} days ago"
  elsif days_until_expiry < warning_days
    status = :warning
    message = "Certificate expires in #{days_until_expiry} days"
  else
    status = :ok
    message = "Certificate valid for #{days_until_expiry} days"
  end
  
  { status: status, days: days_until_expiry, message: message }
end

# Monitor multiple certificates
certificates = {
  'example.com' => '/path/to/example.pem',
  'api.example.com' => '/path/to/api.pem'
}

certificates.each do |domain, path|
  result = check_certificate_expiration(path)
  if result[:status] != :ok
    puts "WARNING - #{domain}: #{result[:message]}"
    # Send alert email
  end
end

Incomplete certificate chains cause validation failures. Servers must send the complete chain from the end-entity certificate through all intermediates to a root CA. Missing intermediate certificates prevent clients from building a valid path:

# Incorrect: sending only the server certificate
cert = OpenSSL::X509::Certificate.new(File.read('server_cert.pem'))

# Correct: concatenate server cert and intermediate certs
full_chain = File.read('server_cert.pem') + 
             File.read('intermediate_cert.pem')
cert = OpenSSL::X509::Certificate.new(full_chain)

# Verify chain completeness
def verify_chain_complete(cert_path)
  content = File.read(cert_path)
  certs = content.scan(/-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m)
  
  certificates = certs.map { |pem| OpenSSL::X509::Certificate.new(pem) }
  
  # Check if chain reaches a self-signed root
  last_cert = certificates.last
  is_complete = last_cert.issuer == last_cert.subject
  
  puts "Chain contains #{certificates.length} certificates"
  puts "Chain complete: #{is_complete}"
  
  certificates.each_with_index do |cert, i|
    puts "#{i + 1}. #{cert.subject}"
  end
  
  is_complete
end

Hostname verification failures occur when the certificate's subject doesn't match the requested hostname. The certificate's Common Name (CN) or Subject Alternative Name (SAN) must match exactly:

def verify_hostname(cert, hostname)
  # Check Common Name
  cn = cert.subject.to_a.find { |e| e[0] == 'CN' }
  return true if cn && cn[1] == hostname
  
  # Check Subject Alternative Names
  san_ext = cert.extensions.find { |e| e.oid == 'subjectAltName' }
  if san_ext
    san_values = san_ext.value.split(',').map(&:strip)
    san_values.each do |san|
      if san.start_with?('DNS:')
        dns_name = san.sub('DNS:', '')
        # Handle wildcards
        if dns_name.start_with?('*.')
          pattern = dns_name.sub('*.', '.*\.')
          return true if hostname.match?(/^#{pattern}$/)
        else
          return true if dns_name == hostname
        end
      end
    end
  end
  
  false
end

# Example usage
cert = OpenSSL::X509::Certificate.new(File.read('cert.pem'))
valid = verify_hostname(cert, 'www.example.com')
puts "Hostname verification: #{valid}"

Private key mismatches prevent certificate usage. The certificate's public key must correspond to the private key used for TLS handshakes:

def verify_key_pair(cert_path, key_path)
  cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
  key = OpenSSL::PKey::RSA.new(File.read(key_path))
  
  # Compare public key modulus
  cert_modulus = cert.public_key.n
  key_modulus = key.public_key.n
  
  if cert_modulus == key_modulus
    puts "Certificate and key match"
    true
  else
    puts "Certificate and key DO NOT match"
    false
  end
rescue OpenSSL::PKey::RSAError
  puts "Invalid private key format"
  false
rescue OpenSSL::X509::CertificateError
  puts "Invalid certificate format"
  false
end

Permission and file access issues cause runtime failures. Private keys require restricted file permissions to prevent unauthorized access:

require 'fileutils'

def secure_key_storage(key_path, key_content)
  # Write key with restrictive permissions
  File.write(key_path, key_content)
  File.chmod(0600, key_path)  # Read/write for owner only
  
  # Verify permissions
  stat = File.stat(key_path)
  mode = stat.mode & 0777
  
  if mode != 0600
    warn "WARNING: Key file has insecure permissions: #{mode.to_s(8)}"
  end
end

# Check for common permission issues
def audit_certificate_files(cert_dir)
  issues = []
  
  Dir.glob("#{cert_dir}/*.{key,pem}") do |file|
    stat = File.stat(file)
    mode = stat.mode & 0777
    
    if file.end_with?('.key') && mode != 0600
      issues << "#{file}: Private key too permissive (#{mode.to_s(8)})"
    end
    
    if !stat.owned_by_current_user?
      issues << "#{file}: Not owned by current user"
    end
  end
  
  issues
end

Tools & Ecosystem

OpenSSL command-line tools provide certificate generation, conversion, and inspection capabilities. These commands handle common certificate operations outside application code:

# Generate self-signed certificate
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

# Generate CSR
openssl req -new -key private.key -out request.csr

# View certificate details
openssl x509 -in certificate.pem -text -noout

# Convert between formats
openssl x509 -in cert.pem -outform DER -out cert.der
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem

# Verify certificate chain
openssl verify -CAfile ca-bundle.pem certificate.pem

# Check certificate and key match
openssl x509 -in cert.pem -modulus -noout | openssl md5
openssl rsa -in key.pem -modulus -noout | openssl md5

# Connect and view server certificate
openssl s_client -connect example.com:443 -showcerts

The openssl gem provides Ruby bindings to OpenSSL library functionality. This standard library gem requires no additional installation and handles cryptographic operations:

require 'openssl'

# Available classes
OpenSSL::X509::Certificate    # Certificate operations
OpenSSL::X509::Request        # Certificate signing requests  
OpenSSL::X509::Store          # Certificate store and validation
OpenSSL::X509::Name           # Distinguished names
OpenSSL::PKey::RSA           # RSA key operations
OpenSSL::PKey::EC            # Elliptic curve keys
OpenSSL::PKCS12              # PKCS#12 archives
OpenSSL::PKCS7               # S/MIME operations

Let's Encrypt provides automated certificate issuance through the ACME protocol. The service issues certificates valid for 90 days, encouraging automation. Several Ruby gems implement ACME clients:

# Using acme-client gem
gem 'acme-client'

require 'acme-client'

# Set up client
client = Acme::Client.new(
  private_key: OpenSSL::PKey::RSA.new(4096),
  directory: Acme::Client::LETSENCRYPT_PRODUCTION_URL
)

# The certbot tool provides command-line automation
# certbot certonly --webroot -w /var/www/html -d example.com

# Using dehydrated shell script for simple automation
# ./dehydrated --cron --domain example.com

Certificate management platforms handle certificate lifecycle operations at scale. These tools track expiration dates, automate renewal, and centralize certificate storage. Popular options include:

  • HashiCorp Vault: Provides PKI secrets engine for certificate generation
  • cert-manager: Kubernetes-native certificate management
  • AWS Certificate Manager: Managed certificates for AWS resources
  • Smallstep: Private certificate authority and automation tools

Ruby integration with these platforms varies by tool:

# Vault PKI integration
require 'vault'

Vault.address = 'https://vault.example.com'
Vault.token = ENV['VAULT_TOKEN']

# Request certificate from Vault PKI backend
result = Vault.logical.write(
  'pki/issue/server-role',
  common_name: 'app.example.com',
  ttl: '720h'
)

certificate = result.data[:certificate]
private_key = result.data[:private_key]
ca_chain = result.data[:ca_chain]

Certificate transparency logs provide public records of issued certificates. CT monitors detect mis-issued certificates and unauthorized CAs. Tools query CT logs to find certificates for domains:

# Using crt.sh web interface
curl 'https://crt.sh/?q=example.com&output=json'

# Using certstream for real-time monitoring
gem 'certstream'

The Ruby SSL ecosystem includes gems for specific use cases:

# ssl_scan - analyze SSL/TLS configuration
gem 'ssl_scan'

# sslscan - security assessment
require 'sslscan'
scanner = SSLScan::Scanner.new('example.com')
results = scanner.scan

# puma - production web server with SSL support
gem 'puma'
# config/puma.rb
ssl_bind '0.0.0.0', '443', {
  cert: 'path/to/cert.pem',
  key: 'path/to/key.pem'
}

Reference

Certificate Fields

Field Description Example Value
Version X.509 version number 2 (v3)
Serial Number Unique identifier within CA 04:D2:1F:C3:...
Signature Algorithm Algorithm used for signing SHA256withRSA
Issuer CA that issued certificate CN=DigiCert TLS RSA SHA256 2020 CA1
Validity Not Before Start of validity period 2024-01-15 00:00:00 UTC
Validity Not After End of validity period 2025-01-14 23:59:59 UTC
Subject Certificate holder identity CN=www.example.com, O=Example Corp
Subject Public Key Info Public key and algorithm RSA 2048-bit
Extensions Additional certificate features Subject Alternative Name, Key Usage
Signature CA's digital signature Binary signature data

Common Extensions

Extension Purpose Typical Values
Subject Alternative Name Additional identities DNS:example.com, DNS:www.example.com
Key Usage Permitted key operations Digital Signature, Key Encipherment
Extended Key Usage Specific usage purposes TLS Web Server Authentication
Basic Constraints CA or end-entity designation CA:FALSE or CA:TRUE, pathlen:0
Authority Key Identifier Links to issuer's key keyid:A1:B2:C3:...
Subject Key Identifier Certificate's key identifier D4:E5:F6:...
CRL Distribution Points Revocation list locations URI:http://crl.example.com/root.crl
Authority Information Access CA and OCSP URLs OCSP - URI:http://ocsp.example.com

File Formats

Format Extension Encoding Usage
PEM .pem, .crt, .cer Base64 ASCII Most common, text readable
DER .der, .cer Binary Compact, Windows native
PKCS#7 .p7b, .p7c Base64 or binary Certificate chains
PKCS#12 .pfx, .p12 Binary Certificate with private key

Ruby OpenSSL API Reference

Class Primary Methods Purpose
X509::Certificate new, to_pem, to_der, subject, issuer, not_before, not_after Certificate operations
X509::Request new, sign, verify, subject CSR handling
X509::Store new, add_cert, verify, set_default_paths Certificate validation
X509::Name parse, to_s, to_a Distinguished name handling
PKey::RSA new, public_key, private?, export RSA key operations
PKey::EC new, generate_key, group Elliptic curve keys

Validation Error Codes

OpenSSL Error Constant Meaning
Certificate has expired V_ERR_CERT_HAS_EXPIRED Past not_after date
Certificate is not yet valid V_ERR_CERT_NOT_YET_VALID Before not_before date
Unable to get issuer certificate V_ERR_UNABLE_TO_GET_ISSUER_CERT Missing CA certificate
Self signed certificate V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT Root not trusted
Self signed certificate in chain V_ERR_SELF_SIGNED_CERT_IN_CHAIN Intermediate not trusted
Certificate signature failure V_ERR_CERT_SIGNATURE_FAILURE Invalid signature
Certificate revoked V_ERR_CERT_REVOKED Listed in CRL/OCSP

Common Ruby Patterns

# Load certificate from file
cert = OpenSSL::X509::Certificate.new(File.read('cert.pem'))

# Create certificate store with system CAs
store = OpenSSL::X509::Store.new
store.set_default_paths

# Validate certificate
result = store.verify(cert)

# Extract certificate fingerprint
fingerprint = Digest::SHA256.hexdigest(cert.to_der)

# Check expiration
days_remaining = ((cert.not_after - Time.now) / 86400).to_i

# Compare certificate and key
cert.public_key.to_pem == private_key.public_key.to_pem