Class: CertificateDepot::Certificate
- Inherits:
-
Object
- Object
- CertificateDepot::Certificate
- Defined in:
- lib/certificate_depot/certificate.rb
Overview
Represents an OpenSSL certificate. TLS/SSL certificates are a rather complicated mix of several standards. If you’re not familiar with TLS certificates, it might help to start by reading the Wikipedia article on the subject: en.wikipedia.org/wiki/Public_key_certificate.
certificate = CertificateDepot::Certificate.from_file('server.pem')
certificate.issuer
Constant Summary collapse
- DEFAULT_VALIDITY_PERIOD =
Our generated certificates are valid for 10 years by default.
3600 * 24 * 365 * 10
- X509v3 =
We create version 3 certificates. Count starts a 0 so 2 is actually 3.
2
- ATTRIBUTE_MAP =
Used to map programmer readable attributes to X509 subject attributes.
{ :common_name => 'CN', :locality_name => 'L', :state_or_province_name => 'ST', :organization => 'O', :organizational_unit_name => 'OU', :country_name => 'C', :street_address => 'STREET', :domain_component => 'DC', :user_id => 'UID', :email_address => 'emailAddress' }
Instance Attribute Summary collapse
-
#certificate ⇒ Object
Returns the value of attribute certificate.
Class Method Summary collapse
-
.from_file(path) ⇒ Object
Instantiates a new instance using certificate data read from file.
-
.generate(attributes = {}) ⇒ Object
Shortcut for CertificateDepot::Certificate#generate.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Used to support easy querying of subject attributes.
-
#generate(attributes = {}) ⇒ Object
Generates a new certificate.
-
#initialize(certificate = nil) ⇒ Certificate
constructor
Creates a new certificate instance.
-
#issuer ⇒ Object
Returns the issuer for the certificate.
-
#method_missing(method, *attributes, &block) ⇒ Object
Used to support easy querying of subject attributes.
-
#public_key ⇒ Object
Returns the public key in the certificate.
-
#serial_number ⇒ Object
Returns the serial number of the certificate.
-
#subject ⇒ Object
Returns the subject of the certificate.
-
#write_to(path) ⇒ Object
Writes the certificate to file.
Constructor Details
#initialize(certificate = nil) ⇒ Certificate
Creates a new certificate instance. The certificate argument should be a OpenSSL::X509::Certificate instance.
33 34 35 |
# File 'lib/certificate_depot/certificate.rb', line 33 def initialize(certificate=nil) @certificate = certificate end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *attributes, &block) ⇒ Object
Used to support easy querying of subject attributes. See ATTRIBUTE_MAP for a complete list of supported attributes.
certificate.common_name #=> '*.example.com'
208 209 210 211 212 213 214 |
# File 'lib/certificate_depot/certificate.rb', line 208 def method_missing(method, *attributes, &block) if x509_attribute = ATTRIBUTE_MAP[method.to_sym] self[x509_attribute] else super end end |
Instance Attribute Details
#certificate ⇒ Object
Returns the value of attribute certificate.
29 30 31 |
# File 'lib/certificate_depot/certificate.rb', line 29 def certificate @certificate end |
Class Method Details
.from_file(path) ⇒ Object
Instantiates a new instance using certificate data read from file.
235 236 237 |
# File 'lib/certificate_depot/certificate.rb', line 235 def self.from_file(path) new(OpenSSL::X509::Certificate.new(File.read(path))) end |
.generate(attributes = {}) ⇒ Object
Shortcut for CertificateDepot::Certificate#generate. See this method for more details.
228 229 230 231 232 |
# File 'lib/certificate_depot/certificate.rb', line 228 def self.generate(attributes={}) certificate = new certificate.generate(attributes) certificate end |
Instance Method Details
#[](key) ⇒ Object
Used to support easy querying of subject attributes. See ATTRIBUTE_MAP for a complete list of supported attributes.
certificate['common_name'] #=> '*.example.com'
220 221 222 223 224 |
# File 'lib/certificate_depot/certificate.rb', line 220 def [](key) @certificate.subject.to_a.each do |name, value, type| return value if name == key end; nil end |
#generate(attributes = {}) ⇒ Object
Generates a new certificate. Possible and compulsory attributes depend on the type of certificate.
Client certificate
Client certificates are used to authenticate a client to a server. They are generated by setting the :type
attribute to :client
.
Compulsory
-
:ca_certificate
- A CertificateDepot::Certificate instance representing the Certification Authority. -
:serial_number
- The serial number to store in the certificate. This number should be unique for certificates issued by the CA. -
:public_key
- A public key which is the sister key of the private key used by the client.
Options
You can choose to either supply an instance of OpenSSL::X509::Name as the :subject
attribute or supply any of the attributes listed in ATTRIBUTE_MAP to generate the subject name. For example:
generate(:common_name => 'John Doe', :email_address => '[email protected]')
Server certificate
Server certificates are used to authenticate a server to a client and set up a secure socket. They are generated by setting the :type
attribute to :server
.
Compulsory
-
:ca_certificate
- A CertificateDepot::Certificate instance representing the Certification Authority. -
:serial_number
- The serial number to store in the certificate. This number should be unique for certificates issued by the CA. -
:public_key
- A public key which is the sister key of the private key used by the client. -
:common_name
- The common name has to match the hostname used for the server. It has to be either a complete match or a wildcard match. So*.example.com
will matchwww.example.com
andmail.example.com
butexample.com
will only matchexample.com
.
Options
If you want to supply an instance of OpenSSL::X509::Name as the :subject
of the certificate, please make sure you set the CN attribute to the correct value of the certificate will be worthless.
You can choose to set any of the other X509 attributes as found in the ATTRIBUTE_MAP, but none of the are strictly necessary.
Certification Authority certificate
CA certificates are used to sign all certificates issued by the CA. They are generated by setting the :type
to :ca
.
Options
You can choose to either supply an instance of OpenSSL::X509::Name as the :subject
attribute or supply any of the attributes listed in ATTRIBUTE_MAP to generate the subject name. For example:
subject = OpenSSL::X509::Name.new
subject.add_entry('CN', 'Certificate Depot CA')
generate(:subject => subject)
generate(:common_name => 'Certificate Depot CA')
Note that this name will be used for both the subject and issuer field in the certificate because it’s a so-called sign-signed certificate.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 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 |
# File 'lib/certificate_depot/certificate.rb', line 114 def generate(attributes={}) from = Time.now to = Time.now + DEFAULT_VALIDITY_PERIOD name = attributes[:subject] || OpenSSL::X509::Name.new ATTRIBUTE_MAP.each do |internal, x509_attribute| name.add_entry(x509_attribute, attributes[internal]) if attributes[internal] end case attributes[:type] when :client, :server issuer = attributes[:ca_certificate].subject serial = attributes[:serial_number] when :ca issuer = name serial = 0 else raise ArgumentError, "Unknown certificate type #{attributes[:type]}, please specify either :client, :server, or :ca" end raise ArgumentError, "Please supply a serial number for the certificate to generate" unless serial @certificate = OpenSSL::X509::Certificate.new @certificate.subject = name @certificate.issuer = issuer @certificate.not_before = from @certificate.not_after = to @certificate.version = X509v3 @certificate.public_key = attributes[:public_key] @certificate.serial = serial extensions = [] factory = OpenSSL::X509::ExtensionFactory.new factory.subject_certificate = @certificate case attributes[:type] when :server factory.issuer_certificate = attributes[:ca_certificate].certificate extensions << factory.create_extension('basicConstraints', 'CA:FALSE', true) extensions << factory.create_extension('keyUsage', 'digitalSignature,keyEncipherment') extensions << factory.create_extension('extendedKeyUsage', 'serverAuth,clientAuth,emailProtection') when :client factory.issuer_certificate = attributes[:ca_certificate].certificate extensions << factory.create_extension('basicConstraints', 'CA:FALSE', true) extensions << factory.create_extension('keyUsage', 'nonRepudiation,digitalSignature,keyEncipherment') extensions << factory.create_extension('extendedKeyUsage', 'clientAuth') when :ca factory.issuer_certificate = @certificate extensions << factory.create_extension('basicConstraints', 'CA:TRUE', true) extensions << factory.create_extension('keyUsage', 'cRLSign,keyCertSign') end extensions << factory.create_extension('subjectKeyIdentifier', 'hash') extensions << factory.create_extension('authorityKeyIdentifier', 'keyid,issuer:always') @certificate.extensions = extensions if attributes[:private_key] @certificate.sign(attributes[:private_key], OpenSSL::Digest::SHA1.new) end @certificate end |
#issuer ⇒ Object
Returns the issuer for the certificate.
190 191 192 |
# File 'lib/certificate_depot/certificate.rb', line 190 def issuer @certificate.issuer end |
#public_key ⇒ Object
Returns the public key in the certificate.
185 186 187 |
# File 'lib/certificate_depot/certificate.rb', line 185 def public_key @certificate.public_key end |
#serial_number ⇒ Object
Returns the serial number of the certificate.
200 201 202 |
# File 'lib/certificate_depot/certificate.rb', line 200 def serial_number @certificate.serial end |
#subject ⇒ Object
Returns the subject of the certificate.
195 196 197 |
# File 'lib/certificate_depot/certificate.rb', line 195 def subject @certificate.subject end |
#write_to(path) ⇒ Object
Writes the certificate to file. The path should be a filename pointing to an existing directory. Note that this will overwrite files without asking.
180 181 182 |
# File 'lib/certificate_depot/certificate.rb', line 180 def write_to(path) File.open(path, 'w') { |file| file.write(@certificate.to_pem) } end |