Skip to content

Instantly share code, notes, and snippets.

@sstelfox
Last active March 25, 2017 21:33
Show Gist options
  • Select an option

  • Save sstelfox/70938206c84634fb6f82f144bebfd8ed to your computer and use it in GitHub Desktop.

Select an option

Save sstelfox/70938206c84634fb6f82f144bebfd8ed to your computer and use it in GitHub Desktop.
I needed ECDSA certs with a chain of custom CAs and client certificates and I already had most of this available. Could be prettier but it works fast and well
#!/usr/bin/env ruby
require 'openssl'
require 'securerandom'
# Work around a broken core library
OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
CERT_DIR = 'generated_certs'
DIGEST = OpenSSL::Digest::SHA384
SRV_CA_CN = 'Server CA'
CLI_CA_CN = 'Client CA'
CLIENT_CN = SecureRandom.uuid
SERVER_CN = 'localhost'
ROOT_CA_CN = 'Root CA'
CLIENT_CA_CRT = File.join(CERT_DIR, 'client_ca.ecdsa.crt')
CLIENT_CA_KEY = File.join(CERT_DIR, 'client_ca.ecdsa.key')
CLIENT_CRT = File.join(CERT_DIR, 'client.ecdsa.crt')
CLIENT_KEY = File.join(CERT_DIR, 'client.ecdsa.key')
ROOT_CA_CRT = File.join(CERT_DIR, 'root_ca.ecdsa.crt')
ROOT_CA_KEY = File.join(CERT_DIR, 'root_ca.ecdsa.key')
SERVER_CA_CRT = File.join(CERT_DIR, 'server_ca.ecdsa.crt')
SERVER_CA_KEY = File.join(CERT_DIR, 'server_ca.ecdsa.key')
SERVER_CRT = File.join(CERT_DIR, 'server.ecdsa.crt')
SERVER_KEY = File.join(CERT_DIR, 'server.ecdsa.key')
def cert_base
cert = OpenSSL::X509::Certificate.new
cert.version = 2
# Good for three years
cert.not_after = (Time.now + (86400 * 365 * 3))
cert.not_before = (Time.now - 86400)
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
[cert, ef]
end
def generate_ca_cert(cn, pkey, path, root_crt = nil, root_key = nil)
return load_cert(path) if File.exists?(path)
cert, ef = cert_base
cert.serial = serial
cert.subject = OpenSSL::X509::Name.parse("C=US, O=Test Server Certs Inc., CN=#{cn}")
cert.issuer = root_crt ? root_crt.subject : cert.subject
cert.public_key = public_key(pkey)
ef.issuer_certificate = root_crt ? root_crt : cert
path_length = root_key ? ",pathlen:0" : ""
cert.add_extension(ef.create_extension('basicConstraints', "CA:true#{path_length}", true))
cert.add_extension(ef.create_extension('keyUsage', 'cRLSign,keyCertSign', true))
cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
# Note: This needs to be after the subjectKeyIdentifier extension
cert.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always', false))
cert.sign((root_key ? root_key : pkey), DIGEST.new)
File.write(path, cert.to_pem)
File.chmod(0600, path)
cert
end
def generate_cli_cert(cn, pkey, path, issuer_crt, issuer_key)
return load_cert(path) if File.exists?(path)
cert, ef = cert_base
cert.serial = serial
cert.subject = OpenSSL::X509::Name.parse("C=US, O=Test Server Certs Inc., CN=#{cn}")
cert.issuer = issuer_crt.subject
cert.public_key = public_key(pkey)
ef.issuer_certificate = issuer_crt
cert.add_extension(ef.create_extension('basicConstraints', 'CA:false', true))
cert.add_extension(ef.create_extension('extendedKeyUsage', 'serverAuth,clientAuth'))
cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature,keyAgreement,nonRepudiation', true))
cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
# Note: This needs to be after the subjectKeyIdentifier extension
cert.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always', false))
# Private Sample OIDs
asn_id = OpenSSL::ASN1::ASN1Data.new(SecureRandom.uuid, OpenSSL::ASN1::UTF8STRING, :APPLICATION)
cert.add_extension(OpenSSL::X509::Extension.new('1.3.6.1.4.1.0.1.1', asn_id))
asn_id = OpenSSL::ASN1::ASN1Data.new(CLIENT_CN, OpenSSL::ASN1::UTF8STRING, :APPLICATION)
cert.add_extension(OpenSSL::X509::Extension.new('1.3.6.1.4.1.0.1.2', asn_id))
cert.sign(issuer_key, DIGEST.new)
File.write(path, cert.to_pem)
File.chmod(0600, path)
cert
end
def generate_srv_cert(cn, pkey, path, issuer_crt, issuer_key)
return load_cert(path) if File.exists?(path)
cert, ef = cert_base
cert.serial = serial
cert.subject = OpenSSL::X509::Name.parse("C=US, O=Test Server Certs Inc., CN=#{cn}")
cert.issuer = issuer_crt.subject
cert.public_key = public_key(pkey)
ef.issuer_certificate = issuer_crt
cert.add_extension(ef.create_extension('basicConstraints', 'CA:false', true))
cert.add_extension(ef.create_extension('extendedKeyUsage', 'serverAuth'))
cert.add_extension(ef.create_extension('keyUsage', 'digitalSignature,keyAgreement,nonRepudiation', true))
cert.add_extension(ef.create_extension('subjectKeyIdentifier', 'hash', false))
# Note: This needs to be after the subjectKeyIdentifier extension
cert.add_extension(ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always', false))
# As of RFC6125 CN is ignored if sans are present
sans = %w( DNS:localhost DNS:localhost4 DNS:localhost6 IP:127.0.0.1 IP:0:0:0:0:0:0:0:1)
cert.add_extension(ef.create_extension('subjectAltName', sans.join(', '), false))
cert.sign(issuer_key, DIGEST.new)
File.write(path, cert.to_pem)
File.chmod(0600, path)
cert
end
def gen_key
key = OpenSSL::PKey::EC.new('secp384r1')
key.generate_key
key
end
def get_key(path)
return load_key(path) if File.exists?(path)
key = gen_key
File.write(path, key.to_pem)
File.chmod(0600, path)
key
end
def load_cert(path)
OpenSSL::X509::Certificate.new(File.read(path))
end
def load_key(path)
OpenSSL::PKey::EC.new(File.read(path))
end
def public_key(priv_key)
key = OpenSSL::PKey::EC.new('secp384r1')
key.public_key = priv_key.public_key
key
end
def serial
(Time.now.to_f * 100_000).to_i
end
Dir.mkdir(CERT_DIR, 0700) unless File.directory?(CERT_DIR)
root_ca_key = get_key(ROOT_CA_KEY)
root_ca_crt = generate_ca_cert(ROOT_CA_CN, root_ca_key, ROOT_CA_CRT)
server_ca_key = get_key(SERVER_CA_KEY)
server_ca_crt = generate_ca_cert(SRV_CA_CN, server_ca_key, SERVER_CA_CRT, root_ca_crt, root_ca_key)
server_key = get_key(SERVER_KEY)
server_crt = generate_srv_cert(SERVER_CN, server_key, SERVER_CRT, server_ca_crt, server_ca_key)
client_ca_key = get_key(CLIENT_CA_KEY)
client_ca_crt = generate_ca_cert(CLI_CA_CN, client_ca_key, CLIENT_CA_CRT, root_ca_crt, root_ca_key)
client_key = get_key(CLIENT_KEY)
client_crt = generate_cli_cert(CLIENT_CN, client_key, CLIENT_CRT, client_ca_crt, client_ca_key)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment