Class: CertificateDepot

Inherits:
Object
  • Object
show all
Defined in:
lib/certificate_depot.rb,
lib/certificate_depot/log.rb,
lib/certificate_depot/store.rb,
lib/certificate_depot/runner.rb,
lib/certificate_depot/server.rb,
lib/certificate_depot/worker.rb,
lib/certificate_depot/keypair.rb,
lib/certificate_depot/certificate.rb

Overview

CertificateDepot

The CertificateDepot manages a single depot of certificates. For more than a casual understanding of these terms we need to explain a bit more about certificates first. If you already know how PKI works, you can skip the following paragraphs.

Certificate Authorities

“[A] certificate authority or certification authority (CA) is an entity that issues digital certificates for use by other parties.” – en.wikipedia.org/wiki/Certificate_authority

When a CA says that party A is who they say they are and you trust the CA then you can assume they are in fact party A.

In a Public Key Infrastructure this means that the CA has a private key which only he knows. He uses this key to sign certificates stating that the person owning the certificate is who the certificate says they are. Because there is a public key associated with the certificate anyone can use a crytographic challenge to make sure the owner has the private key to this certificate.

For example: I have a certificate that says that I'm the person with the email address [email protected] I show my certificate to Rick. Rick gets my public key from the certificate and sends me a challenge. I compute the right response and send it to Rick. Rick checks the response and knows I'm the rightful owner of the certificate.

A PKI infrastructure has many more uses, but we will only focus on authentication in our examples.

There are two ways in which a CA can make sure his trust network stays valid. Each certificate has a limited period in which it's valid. Even if the CA forgets that he has issued the certificate it will stop being valid after a while. Large CA's claim this also protects against evolving attack vectors against the crytography used behind the certificates. Each certificate can be revoked by the CA. A publicly available list of revoked certificates makes it possible to check whether a certain certificate is still valid according to the CA.

Certificate types

Certificate authorities came up with lots of additional features. One of these is a certificate type. To be more precise, there is an extension to the certificate that tells in which cases you should accept the key contained in the certificate. This allows the CA to limit the use of a certificate to just digital signatures, encipherment, or certain types of authentication.

For simplicity Certificate Depot only knows three types; CA, Server, and Client.

A certificate depot

We've named the collection of all information needed to run a CA a certificate depot. It holds the CA certificate and private key, all issued certificates, the revokation list, and several files need to manage these.

Defined Under Namespace

Classes: Certificate, Keypair, Log, Runner, Server, Store, Worker

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ CertificateDepot

Initialize a new depot with the path to the depot directory.

depot = CertificateDepot.new('/var/lib/certificate-depot/example')

75
76
77
# File 'lib/certificate_depot.rb', line 75

def initialize(path)
  @config = OpenSSL::Config.load(self.class.openssl_config_path(path))
end

Class Method Details

.certificate_path(path) ⇒ Object

Returns the path to the CA's certificate given the depot path.


258
259
260
# File 'lib/certificate_depot.rb', line 258

def self.certificate_path(path)
  File.join(certificates_path(path), 'ca.crt')
end

.certificates_path(path) ⇒ Object

Returns the path to the generated certificates given the depot path.


243
244
245
# File 'lib/certificate_depot.rb', line 243

def self.certificates_path(path)
  File.join(path, 'certificates')
end

.configuration_example(path) ⇒ Object

Returns a string with an Apache configuration example for using TLS client certificate authentication.


204
205
206
207
208
209
210
# File 'lib/certificate_depot.rb', line 204

def self.configuration_example(path)
  "SSLEngine on
SSLOptions +StdEnvVars
SSLCertificateFile      \"/etc/apache/ssl/certificates/example.com.pem\"
SSLVerifyClient require
SSLCACertificateFile    \"#{certificate_path(path)}\""
end

.create(path, label, options = {}) ⇒ Object

Creates a new depot on disk.

depot = CertificateDepot.create('/var/lib/certificate-depot/example')

176
177
178
179
180
181
182
# File 'lib/certificate_depot.rb', line 176

def self.create(path, label, options={})
  attributes = options
  create_directories(path)
  create_configuration(path, label)
  create_ca_certificate(path, label)
  new(path)
end

.create_ca_certificate(path, label) ⇒ Object

Creates a CA certificate and keypair and writes it to disk.


160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/certificate_depot.rb', line 160

def self.create_ca_certificate(path, label)
  keypair = CertificateDepot::Keypair.generate
  keypair.write_to(key_path(path))
  
  attributes = {}
  attributes[:type]         = :ca
  attributes[:public_key]   = keypair.public_key
  attributes[:private_key]  = keypair.private_key
  attributes[:organization] = label
  certificate = CertificateDepot::Certificate.generate(attributes)
  certificate.write_to(certificate_path(path))
end

.create_configuration(path, label) ⇒ Object

Writes a configuration file to disk containing the path to the depot and its name.


148
149
150
151
152
153
154
155
156
157
# File 'lib/certificate_depot.rb', line 148

def self.create_configuration(path, label)
  File.open(openssl_config_path(path), 'w') do |file|
    file.write("[ ca ]
label           = #{label}

[ #{label} ]
path            = #{path}
")
  end
end

.create_directories(path) ⇒ Object


139
140
141
142
143
144
# File 'lib/certificate_depot.rb', line 139

def self.create_directories(path)
  FileUtils.mkdir_p(certificates_path(path))
  FileUtils.mkdir_p(private_path(path))
  FileUtils.chmod(0700, private_path(path))
  FileUtils.chmod(0755, path)
end

.crl_path(path) ⇒ Object

Returns the path to the certificate revokation list given the depot path.


248
249
250
# File 'lib/certificate_depot.rb', line 248

def self.crl_path(path)
  File.join(path, 'crl.pem')
end

.generate_keypair_and_certificate(path, options = {}) ⇒ Object

Generates a new RSA keypair and certificate. See CertificateDepot#generate_keypair_and_certificate and CertificateDepot::Certificate.new for more information and possible options.

keypair, certificate = 
  CertificateDepot.generate_keypair_and_certificate(
    '/var/lib/certificate-depot/example', {
      :type => :client,
      :common_name => 'Robert Verkey',
      :email_address => '[email protected]'
    }
  )

197
198
199
200
# File 'lib/certificate_depot.rb', line 197

def self.generate_keypair_and_certificate(path, options={})
  depot = new(path)
  depot.generate_keypair_and_certificate(options)
end

.key_path(path) ⇒ Object

Returns the path to the CA's private key given the depot path.


253
254
255
# File 'lib/certificate_depot.rb', line 253

def self.key_path(path)
  File.join(private_path(path), 'ca.key')
end

.openssl_config_path(path) ⇒ Object

Returns the path to the configuration file given the depot path.


233
234
235
# File 'lib/certificate_depot.rb', line 233

def self.openssl_config_path(path)
  File.join(path, 'depot.cnf')
end

.private_path(path) ⇒ Object

Returns the path to the directory with private data given the depot path.


238
239
240
# File 'lib/certificate_depot.rb', line 238

def self.private_path(path)
  File.join(path, 'private')
end

.run(argv) ⇒ Object

Runs a command to the depot. Used by the command line tool to run commands.


226
227
228
229
230
# File 'lib/certificate_depot.rb', line 226

def self.run(argv)
  runner = ::CertificateDepot::Runner.new(argv)
  runner.run
  runner
end

.start(path, options = {}) ⇒ Object

Starts a server. For available options see CertificateDepot::Server.new.


213
214
215
# File 'lib/certificate_depot.rb', line 213

def self.start(path, options={})
  CertificateDepot::Server.start(new(path), options)
end

.stop(options = {}) ⇒ Object

Stops a running server. Using the options you can specify where to look for the pid file. See CertificateDepot::Server.new for more information about the options.


220
221
222
# File 'lib/certificate_depot.rb', line 220

def self.stop(options={})
  CertificateDepot::Server.stop(options)
end

Instance Method Details

#ca_certificateObject

Returns an instance of CertificateDepot::Certificate containing the certificate of the certificate authority.


92
93
94
# File 'lib/certificate_depot.rb', line 92

def ca_certificate
  @ca_certificate ||= CertificateDepot::Certificate.from_file(self.class.certificate_path(path))
end

#ca_private_keyObject

Returns an instance of OpenSSL::PKey::RSA containing the private key of the certificate authority.


98
99
100
# File 'lib/certificate_depot.rb', line 98

def ca_private_key
  @ca_private_key ||= OpenSSL::PKey::RSA.new(File.read(self.class.key_path(path)))
end

#certificatesObject

Returns an instance of CertificateDepot::Store representing all certificates in the depot.


132
133
134
135
136
137
# File 'lib/certificate_depot.rb', line 132

def certificates
  if @certificates.nil?
    @certificates = CertificateDepot::Store.new(self.class.certificates_path(path))
    @certificates.extend(MonitorMixin)
  end; @certificates
end

#generate_keypair_and_certificate(options = {}) ⇒ Object

Generates a new RSA keypair and certificate.

Defaults

By default the certificate issuer is the CA of the depot and it's also signed by the CA. The serial number is the next available serial number in the depot.

See CertificateDepot::Certificate#generate for all available options.


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/certificate_depot.rb', line 111

def generate_keypair_and_certificate(options={})
  keypair     = CertificateDepot::Keypair.generate
  certificate = nil
  
  certificates.synchronize do
    attributes = options
    attributes[:ca_certificate] = ca_certificate
    attributes[:public_key]     = keypair.public_key
    attributes[:private_key]    = ca_private_key
    attributes[:serial_number]  = certificates.next_serial_number
    certificate = CertificateDepot::Certificate.generate(attributes)
    
    certificates << certificate
    certificates.sync
  end
  
  [keypair, certificate]
end

#labelObject

Returns the label with a descriptive name for the depot. This is usually the common name of the CA.


81
82
83
# File 'lib/certificate_depot.rb', line 81

def label
  @config['ca']['label']
end

#pathObject

Path to the depot directory.


86
87
88
# File 'lib/certificate_depot.rb', line 86

def path
  @config[label]['path']
end