Class: Localhost::Issuer

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

Overview

Represents a local Root Certificate Authority used to sign development certificates.

Constant Summary collapse

BITS =

The default number of bits for the private key. 4096 bits.

4096
VALIDITY =

The default validity period for the certificate. 10 years in seconds.

10 * 365 * 24 * 60 * 60
NAME =

The default certificate issuer name.

"development"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, path: State.path) ⇒ Issuer

Initialize the issuer with the given name.



39
40
41
42
43
44
45
46
# File 'lib/localhost/issuer.rb', line 39

def initialize(name = nil, path: State.path)
	@name = name || NAME
	@path = path
	
	@subject = nil
	@key = nil
	@certificate = nil
end

Class Method Details

.fetch(*arguments, **options) ⇒ Object

Fetch (load or create) a certificate issuer with the given name. See #initialize for the format of the arguments.



22
23
24
25
26
27
28
29
30
# File 'lib/localhost/issuer.rb', line 22

def self.fetch(*arguments, **options)
	issuer = self.new(*arguments, **options)
	
	unless issuer.load
		issuer.save
	end
	
	return issuer
end

Instance Method Details

#certificateObject

The public certificate.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/localhost/issuer.rb', line 78

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

#certificate_pathObject



54
55
56
# File 'lib/localhost/issuer.rb', line 54

def certificate_path
	File.join(@path, "#{@name}.crt")
end

#keyObject



71
72
73
# File 'lib/localhost/issuer.rb', line 71

def key
	@key ||= OpenSSL::PKey::RSA.new(BITS)
end

#key_pathObject



49
50
51
# File 'lib/localhost/issuer.rb', line 49

def key_path
	File.join(@path, "#{@name}.key")
end

#load(path = @root) ⇒ Object

Load the certificate and key from the given path.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/localhost/issuer.rb', line 109

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

#lockfile_pathObject



125
126
127
# File 'lib/localhost/issuer.rb', line 125

def lockfile_path
	File.join(@path, "#{@name}.lock")
end

#save(path = @root) ⇒ Object

Save the certificate and key to the given path.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/localhost/issuer.rb', line 132

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

#subjectObject



59
60
61
# File 'lib/localhost/issuer.rb', line 59

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

#subject=(subject) ⇒ Object

Set the subject name for the certificate.



66
67
68
# File 'lib/localhost/issuer.rb', line 66

def subject= subject
	@subject = subject
end