class Localhost::Authority

Constants

BITS
SERVER_CIPHERS

Public Class Methods

fetch(*args) click to toggle source
# File lib/localhost/authority.rb, line 30
def self.fetch(*args)
        authority = self.new(*args)
        path = self.path
        
        unless authority.load(path)
                Dir.mkdir(path, 0700) unless File.directory?(path)
                
                authority.save(path)
        end
        
        return authority
end
new(hostname = "localhost") click to toggle source
# File lib/localhost/authority.rb, line 43
def initialize(hostname = "localhost")
        @hostname = hostname
        
        @key = nil
        @name = nil
        @certificate = nil
        @store = nil
end
path() click to toggle source
# File lib/localhost/authority.rb, line 26
def self.path
        File.expand_path("~/.localhost")
end

Public Instance Methods

certificate() click to toggle source
# File lib/localhost/authority.rb, line 78
def certificate
        @certificate ||= OpenSSL::X509::Certificate.new.tap do |certificate|
                certificate.subject = self.name
                # We use the same issuer as the subject, which makes this certificate self-signed:
                certificate.issuer = self.name
                
                certificate.public_key = self.key.public_key
                
                certificate.serial = 1
                certificate.version = 2
                
                certificate.not_before = Time.now
                certificate.not_after = Time.now + (3600 * 24 * 365 * 10)
                
                extension_factory = OpenSSL::X509::ExtensionFactory.new
                extension_factory.subject_certificate = certificate
                extension_factory.issuer_certificate = certificate
                
                certificate.extensions = [
                        extension_factory.create_extension("basicConstraints", "CA:FALSE", true),
                        extension_factory.create_extension("subjectKeyIdentifier", "hash"),
                ]
                
                certificate.add_extension extension_factory.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
                certificate.add_extension extension_factory.create_extension("subjectAltName", "DNS: #{@hostname}")
                
                certificate.sign self.key, OpenSSL::Digest::SHA256.new
        end
end
client_context(*args) click to toggle source
# File lib/localhost/authority.rb, line 141
def client_context(*args)
        OpenSSL::SSL::SSLContext.new(*args).tap do |context|
                context.cert_store = self.store
                
                context.set_params(
                        verify_mode: OpenSSL::SSL::VERIFY_PEER,
                )
        end
end
dh_key() click to toggle source
# File lib/localhost/authority.rb, line 58
def dh_key
        @dh_key ||= OpenSSL::PKey::DH.new(BITS)
end
ecdh_key() click to toggle source
# File lib/localhost/authority.rb, line 54
def ecdh_key
        @ecdh_key ||= OpenSSL::PKey::EC.new "prime256v1"
end
key() click to toggle source
# File lib/localhost/authority.rb, line 62
def key
        @key ||= OpenSSL::PKey::RSA.new(BITS)
end
key=(key) click to toggle source
# File lib/localhost/authority.rb, line 66
def key= key
        @key = key
end
load(path) click to toggle source
# File lib/localhost/authority.rb, line 151
def load(path)
        if File.directory? path
                certificate_path = File.join(path, "#{@hostname}.crt")
                key_path = File.join(path, "#{@hostname}.key")
                
                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))
                
                # Certificates with old version need to be regenerated.
                return false if certificate.version < 2
                
                @certificate = certificate
                @key = key
                
                return true
        end
end
name() click to toggle source
# File lib/localhost/authority.rb, line 70
def name
        @name ||= OpenSSL::X509::Name.parse("/O=Development/CN=#{@hostname}")
end
name=(name) click to toggle source
# File lib/localhost/authority.rb, line 74
def name= name
        @name = name
end
save(path) click to toggle source
# File lib/localhost/authority.rb, line 171
def save(path)
        lockfile_path = File.join(path, "#{@hostname}.lock")
        
        File.open(lockfile_path, File::RDWR|File::CREAT, 0644) do |lockfile|
                lockfile.flock(File::LOCK_EX)
                
                File.write(
                        File.join(path, "#{@hostname}.crt"),
                        self.certificate.to_pem
                )
                
                File.write(
                        File.join(path, "#{@hostname}.key"),
                        self.key.to_pem
                )
        end
end
server_context(*args) click to toggle source
# File lib/localhost/authority.rb, line 117
def server_context(*args)
        OpenSSL::SSL::SSLContext.new(*args).tap do |context|
                context.key = self.key
                context.cert = self.certificate
                
                context.session_id_context = "localhost"
                
                if context.respond_to? :tmp_dh_callback=
                        context.tmp_dh_callback = proc {self.dh_key}
                end
                
                if context.respond_to? :ecdh_curves=
                        context.ecdh_curves = 'P-256:P-384:P-521'
                elsif context.respond_to? :tmp_ecdh_callback=
                        context.tmp_ecdh_callback = proc {self.ecdh_key}
                end
                
                context.set_params(
                        ciphers: SERVER_CIPHERS,
                        verify_mode: OpenSSL::SSL::VERIFY_NONE,
                )
        end
end
store() click to toggle source

The certificate store which is used for validating the server certificate:

# File lib/localhost/authority.rb, line 109
def store
        @store ||= OpenSSL::X509::Store.new.tap do |store|
                store.add_cert(self.certificate)
        end
end