class Flores::PKI::CertificateSigningRequest
A certificate signing request.
From here, you can configure a certificate to be created based on your desired configuration.
Example making a root CA:
key = OpenSSL::PKey::RSA.generate(4096, 65537) csr = Flores::PKI::CertificateSigningRequest.new csr.subject = "OU=Fancy Pants Inc." certificate = csr.create_root(key)
Example making an intermediate CA:
root_key = OpenSSL::PKey::RSA.generate(4096, 65537) root_csr = Flores::PKI::CertificateSigningRequest.new root_csr.subject = "OU=Fancy Pants Inc." root_csr.public_key = root_key.public root_certificate = csr.create_root(root_key) intermediate_key = OpenSSL::PKey::RSA.generate(4096, 65537) intermediate_csr = Flores::PKI::CertificateSigningRequest.new intermediate_csr.public_key = intermediate_key.public intermediate_csr.subject = "OU=Fancy Pants Inc. Intermediate 1" intermediate_certificate = csr.create_intermediate(root_certificate, root_key)
Attributes
digest_method[R]
expire_time[R]
public_key[R]
serial[R]
signing_certificate[R]
signing_key[R]
start_time[R]
subject[R]
subject_alternates[R]
Public Class Methods
new()
click to toggle source
# File lib/flores/pki/csr.rb, line 42 def initialize self.serial = Flores::PKI.random_serial self.digest_method = default_digest_method end
Public Instance Methods
create()
click to toggle source
# File lib/flores/pki/csr.rb, line 136 def create validate! extensions = OpenSSL::X509::ExtensionFactory.new extensions.subject_certificate = certificate extensions.issuer_certificate = self_signed? ? certificate : signing_certificate certificate.issuer = extensions.issuer_certificate.subject certificate.add_extension(extensions.create_extension("subjectKeyIdentifier", "hash", false)) # RFC 5280 4.2.1.1. Authority Key Identifier # This is "who signed this key" certificate.add_extension(extensions.create_extension("authorityKeyIdentifier", "keyid:always", false)) #certificate.add_extension(extensions.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always", false)) if want_signature_ability? # Create a CA. certificate.add_extension(extensions.create_extension("basicConstraints", "CA:TRUE", true)) # Rough googling seems to indicate at least keyCertSign is required for CA and intermediate certs. certificate.add_extension(extensions.create_extension("keyUsage", "keyCertSign, cRLSign, digitalSignature", true)) else # Create a client+server certificate # # It feels weird to create a certificate that's valid as both server and client, but a brief inspection of major # web properties (apple.com, google.com, yahoo.com, github.com, fastly.com, mozilla.com, amazon.com) reveals that # major web properties have certificates with both clientAuth and serverAuth extended key usages. Further, # these major server certificates all have digitalSignature and keyEncipherment for key usage. # # Here's the command I used to check this: # echo mozilla.com apple.com github.com google.com yahoo.com fastly.com elastic.co amazon.com \ # | xargs -n1 sh -c 'openssl s_client -connect $1:443 \ # | sed -ne "/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p" \ # | openssl x509 -text -noout | sed -ne "/X509v3 extensions/,/Signature Algorithm/p" | sed -e "s/^/$1 /"' - \ # | grep -A2 'Key Usage' certificate.add_extension(extensions.create_extension("keyUsage", "digitalSignature, keyEncipherment", true)) certificate.add_extension(extensions.create_extension("extendedKeyUsage", "clientAuth, serverAuth", false)) end if @subject_alternates certificate.add_extension(extensions.create_extension("subjectAltName", @subject_alternates.join(","))) end certificate.serial = OpenSSL::BN.new(serial) certificate.sign(signing_key, digest_method) certificate end
digest_method=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 205 def digest_method=(value) raise InvalidData, "digest_method must be a OpenSSL::Digest (or a subclass)" unless value.is_a?(OpenSSL::Digest) @digest_method = value end
expire_time=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 87 def expire_time=(value) @expire_time = validate_time(value) end
public_key=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 70 def public_key=(value) @public_key = validate_public_key(value) end
serial=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 211 def serial=(value) begin Integer(value) rescue raise InvalidData, "Invalid serial value. Must be a number (or a String containing only nubers)" end @serial = value end
signing_certificate=(certificate)
click to toggle source
Set the certificate which is going to be signing this request.
# File lib/flores/pki/csr.rb, line 183 def signing_certificate=(certificate) raise InvalidData, "signing_certificate must be an OpenSSL::X509::Certificate" unless certificate.is_a?(OpenSSL::X509::Certificate) @signing_certificate = certificate end
signing_key=(private_key)
click to toggle source
# File lib/flores/pki/csr.rb, line 190 def signing_key=(private_key) raise InvalidData, "signing_key must be an OpenSSL::PKey::PKey (or a subclass)" unless private_key.is_a?(OpenSSL::PKey::PKey) @signing_key = private_key end
start_time=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 81 def start_time=(value) @start_time = validate_time(value) end
subject=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 58 def subject=(value) @subject = validate_subject(value) end
subject_alternates=(values)
click to toggle source
# File lib/flores/pki/csr.rb, line 64 def subject_alternates=(values) @subject_alternates = values end
want_signature_ability=(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 195 def want_signature_ability=(value) raise InvalidData, "want_signature_ability must be a boolean" unless value == true || value == false @want_signature_ability = value end
want_signature_ability?()
click to toggle source
# File lib/flores/pki/csr.rb, line 200 def want_signature_ability? @want_signature_ability == true end
Private Instance Methods
certificate()
click to toggle source
# File lib/flores/pki/csr.rb, line 98 def certificate return @certificate if @certificate @certificate = OpenSSL::X509::Certificate.new # RFC5280 # > 4.1.2.1. Version # > version MUST be 3 (value is 2). # # Version value of '2' means a v3 certificate. @certificate.version = 2 @certificate.subject = subject @certificate.not_before = start_time @certificate.not_after = expire_time @certificate.public_key = public_key @certificate end
default_digest_method()
click to toggle source
# File lib/flores/pki/csr.rb, line 116 def default_digest_method OpenSSL::Digest::SHA256.new end
self_signed?()
click to toggle source
# File lib/flores/pki/csr.rb, line 120 def self_signed? @signing_certificate.nil? end
validate!()
click to toggle source
# File lib/flores/pki/csr.rb, line 124 def validate! if self_signed? if @signing_key.nil? raise InvalidRequest, "No signing_key given. Cannot sign key." end elsif @signing_certificate.nil? && @signing_key raise InvalidRequest, "signing_key given, but no signing_certificate is set" elsif @signing_certificate && @signing_key.nil? raise InvalidRequest, "signing_certificate given, but no signing_key is set" end end
validate_public_key(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 74 def validate_public_key(value) raise InvalidData, "public key must be a OpenSSL::PKey::PKey" unless value.is_a? OpenSSL::PKey::PKey value end
validate_subject(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 49 def validate_subject(value) OpenSSL::X509::Name.parse(value) rescue OpenSSL::X509::NameError => e raise InvalidSubject, "Invalid subject '#{value}'. (#{e})" rescue TypeError => e # Bug(?) in MRI 2.1.6(?) raise InvalidSubject, "Invalid subject '#{value}'. (#{e})" end
validate_time(value)
click to toggle source
# File lib/flores/pki/csr.rb, line 93 def validate_time(value) raise InvalidTime, "#{value.inspect} (class #{value.class.name})" unless value.is_a?(Time) value end