certificate_authority 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ module CertificateAuthority
2
+ class DistinguishedName
3
+ include ActiveModel::Validations
4
+
5
+ validates_presence_of :common_name
6
+
7
+ attr_accessor :common_name
8
+ alias :cn :common_name
9
+
10
+ attr_accessor :locality
11
+ alias :l :locality
12
+
13
+ attr_accessor :state
14
+ alias :s :state
15
+
16
+ attr_accessor :country
17
+ alias :c :country
18
+
19
+ attr_accessor :organization
20
+ alias :o :organization
21
+
22
+ attr_accessor :organizational_unit
23
+ alias :ou :organizational_unit
24
+
25
+ def to_x509_name
26
+ raise "Invalid Distinguished Name" unless valid?
27
+
28
+ # NB: the capitalization in the strings counts
29
+ name = OpenSSL::X509::Name.new
30
+ name.add_entry("CN", common_name)
31
+ name.add_entry("O", organization) unless organization.blank?
32
+ name.add_entry("OU", common_name) unless organizational_unit.blank?
33
+ name.add_entry("S", common_name) unless state.blank?
34
+ name.add_entry("L", common_name) unless locality.blank?
35
+
36
+ name
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,251 @@
1
+ module CertificateAuthority
2
+ module Extensions
3
+ module ExtensionAPI
4
+ def to_s
5
+ raise "Implementation required"
6
+ end
7
+
8
+ def config_extensions
9
+ {}
10
+ end
11
+
12
+ def openssl_identifier
13
+ raise "Implementation required"
14
+ end
15
+ end
16
+
17
+ class BasicContraints
18
+ include ExtensionAPI
19
+ include ActiveModel::Validations
20
+ attr_accessor :ca
21
+ attr_accessor :path_len
22
+ validates :ca, :inclusion => [true,false]
23
+
24
+ def initialize
25
+ self.ca = false
26
+ end
27
+
28
+ def is_ca?
29
+ self.ca
30
+ end
31
+
32
+ def path_len=(value)
33
+ raise "path_len must be a non-negative integer" if value < 0 or !value.is_a?(Fixnum)
34
+ @path_len = value
35
+ end
36
+
37
+ def openssl_identifier
38
+ "basicConstraints"
39
+ end
40
+
41
+ def to_s
42
+ result = ""
43
+ result += "CA:#{self.ca}"
44
+ result += ",pathlen:#{self.path_len}" unless self.path_len.nil?
45
+ result
46
+ end
47
+ end
48
+
49
+ class CrlDistributionPoints
50
+ include ExtensionAPI
51
+
52
+ attr_accessor :uri
53
+
54
+ def initialize
55
+ self.uri = "http://moo.crlendPoint.example.com/something.crl"
56
+ end
57
+
58
+ def openssl_identifier
59
+ "crlDistributionPoints"
60
+ end
61
+
62
+ ## NB: At this time it seems OpenSSL's extension handlers don't support
63
+ ## any of the config options the docs claim to support... everything comes back
64
+ ## "missing value" on GENERAL NAME. Even if copied verbatim
65
+ def config_extensions
66
+ {
67
+ # "custom_crl_fields" => {"fullname" => "URI:#{fullname}"},
68
+ # "issuer_sect" => {"CN" => "crlissuer.com", "C" => "US", "O" => "shudder"}
69
+ }
70
+ end
71
+
72
+ def to_s
73
+ "URI:#{self.uri}"
74
+ end
75
+ end
76
+
77
+ class SubjectKeyIdentifier
78
+ include ExtensionAPI
79
+ def openssl_identifier
80
+ "subjectKeyIdentifier"
81
+ end
82
+
83
+ def to_s
84
+ "hash"
85
+ end
86
+ end
87
+
88
+ class AuthorityKeyIdentifier
89
+ include ExtensionAPI
90
+
91
+ def openssl_identifier
92
+ "authorityKeyIdentifier"
93
+ end
94
+
95
+ def to_s
96
+ "keyid,issuer"
97
+ end
98
+ end
99
+
100
+ class AuthorityInfoAccess
101
+ include ExtensionAPI
102
+
103
+ attr_accessor :ocsp
104
+
105
+ def initialize
106
+ self.ocsp = []
107
+ end
108
+
109
+ def openssl_identifier
110
+ "authorityInfoAccess"
111
+ end
112
+
113
+ def to_s
114
+ "OCSP;URI:#{self.ocsp}"
115
+ end
116
+ end
117
+
118
+ class KeyUsage
119
+ include ExtensionAPI
120
+
121
+ attr_accessor :usage
122
+
123
+ def initialize
124
+ self.usage = ["digitalSignature", "nonRepudiation"]
125
+ end
126
+
127
+ def openssl_identifier
128
+ "keyUsage"
129
+ end
130
+
131
+ def to_s
132
+ "#{self.usage.join(',')}"
133
+ end
134
+ end
135
+
136
+ class ExtendedKeyUsage
137
+ include ExtensionAPI
138
+
139
+ attr_accessor :usage
140
+
141
+ def initialize
142
+ self.usage = ["serverAuth","clientAuth"]
143
+ end
144
+
145
+ def openssl_identifier
146
+ "extendedKeyUsage"
147
+ end
148
+
149
+ def to_s
150
+ "#{self.usage.join(',')}"
151
+ end
152
+ end
153
+
154
+ class SubjectAlternativeName
155
+ include ExtensionAPI
156
+
157
+ attr_accessor :uris
158
+
159
+ def initialize
160
+ self.uris = []
161
+ end
162
+
163
+ def uris=(value)
164
+ raise "URIs must be an array" unless value.is_a?(Array)
165
+ @uris = value
166
+ end
167
+
168
+ def openssl_identifier
169
+ "subjectAltName"
170
+ end
171
+
172
+ def to_s
173
+ if self.uris.empty?
174
+ return ""
175
+ end
176
+ "URI:#{self.uris.join(',URI:')}"
177
+ end
178
+ end
179
+
180
+ class CertificatePolicies
181
+ include ExtensionAPI
182
+
183
+ attr_accessor :policy_identifier
184
+ attr_accessor :cps_uris
185
+ ##User notice
186
+ attr_accessor :explicit_text
187
+ attr_accessor :organization
188
+ attr_accessor :notice_numbers
189
+
190
+ def initialize
191
+ @contains_data = false
192
+ end
193
+
194
+
195
+ def openssl_identifier
196
+ "certificatePolicies"
197
+ end
198
+
199
+ def user_notice=(value={})
200
+ value.keys.each do |key|
201
+ self.send("#{key}=".to_sym, value[key])
202
+ end
203
+ end
204
+
205
+ def config_extensions
206
+ config_extension = {}
207
+ custom_policies = {}
208
+ notice = {}
209
+ unless self.policy_identifier.nil?
210
+ custom_policies["policyIdentifier"] = self.policy_identifier
211
+ end
212
+
213
+ if !self.cps_uris.nil? and self.cps_uris.is_a?(Array)
214
+ self.cps_uris.each_with_index do |cps_uri,i|
215
+ custom_policies["CPS.#{i}"] = cps_uri
216
+ end
217
+ end
218
+
219
+ unless self.explicit_text.nil?
220
+ notice["explicitText"] = self.explicit_text
221
+ end
222
+
223
+ unless self.organization.nil?
224
+ notice["organization"] = self.organization
225
+ end
226
+
227
+ unless self.notice_numbers.nil?
228
+ notice["noticeNumbers"] = self.notice_numbers
229
+ end
230
+
231
+ if notice.keys.size > 0
232
+ custom_policies["userNotice.1"] = "@notice"
233
+ config_extension["notice"] = notice
234
+ end
235
+
236
+ if custom_policies.keys.size > 0
237
+ config_extension["custom_policies"] = custom_policies
238
+ @contains_data = true
239
+ end
240
+
241
+ config_extension
242
+ end
243
+
244
+ def to_s
245
+ return "" unless @contains_data
246
+ "ia5org,@custom_policies"
247
+ end
248
+ end
249
+
250
+ end
251
+ end
@@ -0,0 +1,62 @@
1
+ module CertificateAuthority
2
+ module KeyMaterial
3
+ def public_key
4
+ raise "Required implementation"
5
+ end
6
+
7
+ def private_key
8
+ raise "Required implementation"
9
+ end
10
+
11
+ def is_in_hardware?
12
+ raise "Required implementation"
13
+ end
14
+
15
+ def is_in_memory?
16
+ raise "Required implementation"
17
+ end
18
+ end
19
+
20
+ class MemoryKeyMaterial
21
+ include KeyMaterial
22
+ include ActiveModel::Validations
23
+
24
+ attr_accessor :keypair
25
+ attr_accessor :private_key
26
+ attr_accessor :public_key
27
+
28
+ def initialize
29
+ end
30
+
31
+ validates_each :private_key do |record, attr, value|
32
+ record.errors.add :private_key, "cannot be blank" if record.private_key.nil?
33
+ end
34
+ validates_each :public_key do |record, attr, value|
35
+ record.errors.add :public_key, "cannot be blank" if record.public_key.nil?
36
+ end
37
+
38
+ def is_in_hardware?
39
+ false
40
+ end
41
+
42
+ def is_in_memory?
43
+ true
44
+ end
45
+
46
+ def generate_key(modulus_bits=1024)
47
+ self.keypair = OpenSSL::PKey::RSA.new(modulus_bits)
48
+ self.private_key = keypair
49
+ self.public_key = keypair.public_key
50
+ self.keypair
51
+ end
52
+
53
+ def private_key
54
+ @private_key
55
+ end
56
+
57
+ def public_key
58
+ @public_key
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,77 @@
1
+ module CertificateAuthority
2
+ class OCSPHandler
3
+ include ActiveModel::Validations
4
+
5
+ attr_accessor :ocsp_request
6
+ attr_accessor :certificate_ids
7
+
8
+ attr_accessor :certificates
9
+ attr_accessor :parent
10
+
11
+ attr_accessor :ocsp_response_body
12
+
13
+ validate do |crl|
14
+ errors.add :parent, "A parent entity must be set" if parent.nil?
15
+ end
16
+ validate :all_certificates_available
17
+
18
+ def initialize
19
+ self.certificates = {}
20
+ end
21
+
22
+ def <<(cert)
23
+ self.certificates[cert.serial_number.number.to_s] = cert
24
+ end
25
+
26
+ def extract_certificate_serials
27
+ raise "No valid OCSP request was supplied" if self.ocsp_request.nil?
28
+ openssl_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
29
+
30
+ self.certificate_ids = openssl_request.certid.collect do |cert_id|
31
+ cert_id.serial
32
+ end
33
+
34
+ self.certificate_ids
35
+ end
36
+
37
+
38
+ def response
39
+ raise "Invalid response" unless valid?
40
+
41
+ openssl_ocsp_response = OpenSSL::OCSP::BasicResponse.new
42
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
43
+ openssl_ocsp_response.copy_nonce(openssl_ocsp_request)
44
+
45
+ openssl_ocsp_request.certid.each do |cert_id|
46
+ certificate = self.certificates[cert_id.serial.to_s]
47
+
48
+ openssl_ocsp_response.add_status(cert_id,
49
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0,
50
+ 0, 0, 30, nil)
51
+ end
52
+
53
+
54
+ openssl_ocsp_response.sign(OpenSSL::X509::Certificate.new(self.parent.to_pem), self.parent.key_material.private_key, nil, nil)
55
+ final_response = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, openssl_ocsp_response)
56
+ self.ocsp_response_body = final_response
57
+ self.ocsp_response_body
58
+ end
59
+
60
+ def to_der
61
+ raise "No signed OCSP response body available" if self.ocsp_response_body.nil?
62
+ self.ocsp_response_body.to_der
63
+ end
64
+
65
+ private
66
+
67
+ def all_certificates_available
68
+ openssl_ocsp_request = OpenSSL::OCSP::Request.new(self.ocsp_request)
69
+
70
+ openssl_ocsp_request.certid.each do |cert_id|
71
+ certificate = self.certificates[cert_id.serial.to_s]
72
+ errors.add(:base, "Certificate #{cert_id.serial} has not been added yet") if certificate.nil?
73
+ end
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,65 @@
1
+ module CertificateAuthority
2
+ class Pkcs11KeyMaterial
3
+ include KeyMaterial
4
+ include ActiveModel::Validations
5
+ include ActiveModel::Serialization
6
+
7
+ attr_accessor :engine
8
+ attr_accessor :token_id
9
+ attr_accessor :pkcs11_lib
10
+ attr_accessor :openssl_pkcs11_engine_lib
11
+ attr_accessor :pin
12
+
13
+ def initialize(attributes = {})
14
+ @attributes = attributes
15
+ initialize_engine
16
+ end
17
+
18
+ def is_in_hardware?
19
+ true
20
+ end
21
+
22
+ def is_in_memory?
23
+ false
24
+ end
25
+
26
+ def generate_key(modulus_bits=1024)
27
+ puts "Key generation is not currently supported in hardware"
28
+ nil
29
+ end
30
+
31
+ def private_key
32
+ initialize_engine
33
+ self.engine.load_private_key(self.token_id)
34
+ end
35
+
36
+ def public_key
37
+ initialize_engine
38
+ self.engine.load_public_key(self.token_id)
39
+ end
40
+
41
+ private
42
+
43
+ def initialize_engine
44
+ ## We're going to return early and try again later if params weren't passed in
45
+ ## at initialization. Any attempt at getting a public/private key will try
46
+ ## again.
47
+ return false if self.openssl_pkcs11_engine_lib.nil? or self.pkcs11_lib.nil?
48
+ return self.engine unless self.engine.nil?
49
+ OpenSSL::Engine.load
50
+
51
+ pkcs11 = OpenSSL::Engine.by_id("dynamic") do |e|
52
+ e.ctrl_cmd("SO_PATH",self.openssl_pkcs11_engine_lib)
53
+ e.ctrl_cmd("ID","pkcs11")
54
+ e.ctrl_cmd("LIST_ADD","1")
55
+ e.ctrl_cmd("LOAD")
56
+ e.ctrl_cmd("PIN",self.pin) unless self.pin.nil? or self.pin == ""
57
+ e.ctrl_cmd("MODULE_PATH",self.pkcs11_lib)
58
+ end
59
+
60
+ self.engine = pkcs11
61
+ pkcs11
62
+ end
63
+
64
+ end
65
+ end