Class: Localhost::Authority

Inherits:
Object
  • Object
show all
Defined in:
lib/localhost/authority.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hostname = "localhost") ⇒ Authority

Returns a new instance of Authority.



43
44
45
46
47
48
49
50
# File 'lib/localhost/authority.rb', line 43

def initialize(hostname = "localhost")
  @hostname = hostname
  
  @key = nil
  @name = nil
  @certificate = nil
  @store = nil
end

Class Method Details

.fetch(*args) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
# 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

.pathObject



26
27
28
# File 'lib/localhost/authority.rb', line 26

def self.path
  File.expand_path("~/.localhost")
end

Instance Method Details

#certificateObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/localhost/authority.rb', line 68

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) ⇒ Object



116
117
118
119
120
121
122
123
124
# File 'lib/localhost/authority.rb', line 116

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

#keyObject



52
53
54
# File 'lib/localhost/authority.rb', line 52

def key
  @key ||= OpenSSL::PKey::RSA.new(1024*2)
end

#key=(key) ⇒ Object



56
57
58
# File 'lib/localhost/authority.rb', line 56

def key= key
  @key = key
end

#load(path) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/localhost/authority.rb', line 126

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

#nameObject



60
61
62
# File 'lib/localhost/authority.rb', line 60

def name
  @name ||= OpenSSL::X509::Name.parse("O=Development/CN=#{@hostname}")
end

#name=(name) ⇒ Object



64
65
66
# File 'lib/localhost/authority.rb', line 64

def name= name
  @name = name
end

#save(path) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/localhost/authority.rb', line 146

def save(path)
  File.write(
    File.join(path, "#{@hostname}.crt"),
    self.certificate.to_pem
  )
  
  File.write(
    File.join(path, "#{@hostname}.key"),
    self.key.to_pem
  )
end

#server_context(*args) ⇒ Object



105
106
107
108
109
110
111
112
113
114
# File 'lib/localhost/authority.rb', line 105

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"
    
    context.set_params
  end
end

#storeObject

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



99
100
101
102
103
# File 'lib/localhost/authority.rb', line 99

def store
  @store ||= OpenSSL::X509::Store.new.tap do |store|
    store.add_cert(self.certificate)
  end
end