class Issuer
Represents a local Root Certificate Authority used to sign development certificates.
Definitions
BITS = 4096
The default number of bits for the private key. 4096 bits.
VALIDITY = 10 * 365 * 24 * 60 * 60
The default validity period for the certificate. 10 years in seconds.
def self.fetch(*arguments, **options)
Fetch (load or create) a certificate issuer with the given name.
See #initialize
for the format of the arguments.
Implementation
def self.fetch(*arguments, **options)
issuer = self.new(*arguments, **options)
unless issuer.load
issuer.save
end
return issuer
end
NAME = "development"
The default certificate issuer name.
def initialize(name = nil, path: State.path)
Initialize the issuer with the given name.
Signature
-
parameter
name
String
The common name to use for the certificate.
-
parameter
path
String
The path path for loading and saving the certificate.
Implementation
def initialize(name = nil, path: State.path)
@name = name || NAME
@path = path
@subject = nil
@key = nil
@certificate = nil
end
def key_path
Signature
-
returns
String
The path to the private key.
Implementation
def key_path
File.join(@path, "#{@name}.key")
end
def certificate_path
Signature
-
returns
String
The path to the public certificate.
Implementation
def certificate_path
File.join(@path, "#{@name}.crt")
end
def subject
Signature
-
returns
OpenSSL::X509::Name
The subject name for the certificate.
Implementation
def subject
@subject ||= OpenSSL::X509::Name.parse("/O=localhost.rb/CN=#{@name}")
end
def subject= subject
Set the subject name for the certificate.
Signature
-
parameter
subject
OpenSSL::X509::Name
The subject name for the certificate.
Implementation
def subject= subject
@subject = subject
end
def key
Signature
-
returns
OpenSSL::PKey::RSA
The private key.
Implementation
def key
@key ||= OpenSSL::PKey::RSA.new(BITS)
end
def certificate
The public certificate.
Signature
-
returns
OpenSSL::X509::Certificate
A self-signed certificate.
Implementation
def certificate
@certificate ||= OpenSSL::X509::Certificate.new.tap do |certificate|
certificate.subject = self.subject
# We use the same issuer as the subject, which makes this certificate self-signed:
certificate.issuer = self.subject
certificate.public_key = self.key.public_key
certificate.serial = Time.now.to_i
certificate.version = 2
certificate.not_before = Time.now - 10
certificate.not_after = Time.now + VALIDITY
extension_factory = ::OpenSSL::X509::ExtensionFactory.new
extension_factory.subject_certificate = certificate
extension_factory.issuer_certificate = certificate
certificate.add_extension extension_factory.create_extension("basicConstraints", "CA:TRUE", true)
certificate.add_extension extension_factory.create_extension("keyUsage", "keyCertSign, cRLSign", true)
certificate.add_extension extension_factory.create_extension("subjectKeyIdentifier", "hash")
certificate.add_extension extension_factory.create_extension("authorityKeyIdentifier", "keyid:always", false)
certificate.sign self.key, OpenSSL::Digest::SHA256.new
end
end
def load(path = @root)
Load the certificate and key from the given path.
Signature
-
parameter
path
String
The path to load the certificate and key.
-
returns
Boolean
True if the certificate and key were loaded successfully.
Implementation
def load(path = @root)
certificate_path = self.certificate_path
key_path = self.key_path
return false unless File.exist?(certificate_path) and File.exist?(key_path)
certificate = OpenSSL::X509::Certificate.new(File.read(certificate_path))
key = OpenSSL::PKey::RSA.new(File.read(key_path))
@certificate = certificate
@key = key
return true
end
def lockfile_path
Signature
-
returns
String
The path to the lockfile.
Implementation
def lockfile_path
File.join(@path, "#{@name}.lock")
end
def save(path = @root)
Save the certificate and key to the given path.
Signature
-
parameter
path
String
The path to save the certificate and key.
Implementation
def save(path = @root)
lockfile_path = self.lockfile_path
File.open(lockfile_path, File::RDWR|File::CREAT, 0644) do |lockfile|
lockfile.flock(File::LOCK_EX)
File.write(
self.certificate_path,
self.certificate.to_pem
)
File.write(
self.key_path,
self.key.to_pem
)
end
return true
end