Class: Flores::PKI::CertificateSigningRequest

Inherits:
Object
  • Object
show all
Defined in:
lib/flores/pki/csr.rb

Overview

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)

Defined Under Namespace

Classes: InvalidData, InvalidRequest, InvalidSubject, InvalidTime

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCertificateSigningRequest

Returns a new instance of CertificateSigningRequest.



42
43
44
45
# File 'lib/flores/pki/csr.rb', line 42

def initialize
  self.serial = Flores::PKI.random_serial
  self.digest_method = default_digest_method
end

Instance Attribute Details

#digest_methodObject

Returns the value of attribute digest_method.



204
205
206
# File 'lib/flores/pki/csr.rb', line 204

def digest_method
  @digest_method
end

#expire_timeObject

Returns the value of attribute expire_time.



91
92
93
# File 'lib/flores/pki/csr.rb', line 91

def expire_time
  @expire_time
end

#public_keyObject

Returns the value of attribute public_key.



79
80
81
# File 'lib/flores/pki/csr.rb', line 79

def public_key
  @public_key
end

#serialObject

Returns the value of attribute serial.



210
211
212
# File 'lib/flores/pki/csr.rb', line 210

def serial
  @serial
end

#signing_certificateObject

Returns the value of attribute signing_certificate.



187
188
189
# File 'lib/flores/pki/csr.rb', line 187

def signing_certificate
  @signing_certificate
end

#signing_keyObject

Returns the value of attribute signing_key.



189
190
191
# File 'lib/flores/pki/csr.rb', line 189

def signing_key
  @signing_key
end

#start_timeObject

Returns the value of attribute start_time.



85
86
87
# File 'lib/flores/pki/csr.rb', line 85

def start_time
  @start_time
end

#subjectObject

Returns the value of attribute subject.



62
63
64
# File 'lib/flores/pki/csr.rb', line 62

def subject
  @subject
end

#subject_alternatesObject

Returns the value of attribute subject_alternates.



68
69
70
# File 'lib/flores/pki/csr.rb', line 68

def subject_alternates
  @subject_alternates
end

Instance Method Details

#createObject



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# 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

#want_signature_ability=(value) ⇒ Object

Raises:



195
196
197
198
# 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?Boolean

Returns:

  • (Boolean)


200
201
202
# File 'lib/flores/pki/csr.rb', line 200

def want_signature_ability?
  @want_signature_ability == true
end