certificate_authority 0.1.2 → 1.0.0

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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +2 -8
  6. data/Gemfile.lock +71 -27
  7. data/README.rdoc +184 -89
  8. data/Rakefile +6 -41
  9. data/certificate_authority.gemspec +22 -81
  10. data/lib/certificate_authority.rb +7 -6
  11. data/lib/certificate_authority/certificate.rb +151 -71
  12. data/lib/certificate_authority/certificate_revocation_list.rb +46 -26
  13. data/lib/certificate_authority/core_extensions.rb +46 -0
  14. data/lib/certificate_authority/distinguished_name.rb +84 -17
  15. data/lib/certificate_authority/extensions.rb +483 -96
  16. data/lib/certificate_authority/key_material.rb +75 -21
  17. data/lib/certificate_authority/ocsp_handler.rb +99 -29
  18. data/lib/certificate_authority/pkcs11_key_material.rb +13 -15
  19. data/lib/certificate_authority/revocable.rb +14 -0
  20. data/lib/certificate_authority/serial_number.rb +18 -5
  21. data/lib/certificate_authority/signing_entity.rb +5 -7
  22. data/lib/certificate_authority/signing_request.rb +91 -0
  23. data/lib/certificate_authority/validations.rb +31 -0
  24. data/lib/certificate_authority/version.rb +3 -0
  25. metadata +96 -94
  26. data/VERSION.yml +0 -5
  27. data/spec/spec_helper.rb +0 -4
  28. data/spec/units/certificate_authority_spec.rb +0 -4
  29. data/spec/units/certificate_revocation_list_spec.rb +0 -68
  30. data/spec/units/certificate_spec.rb +0 -351
  31. data/spec/units/distinguished_name_spec.rb +0 -38
  32. data/spec/units/extensions_spec.rb +0 -53
  33. data/spec/units/key_material_spec.rb +0 -96
  34. data/spec/units/ocsp_handler_spec.rb +0 -104
  35. data/spec/units/serial_number_spec.rb +0 -20
  36. data/spec/units/signing_entity_spec.rb +0 -4
  37. data/spec/units/units_helper.rb +0 -1
@@ -1,351 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::Certificate do
4
- before(:each) do
5
- @certificate = CertificateAuthority::Certificate.new
6
- end
7
-
8
- describe CertificateAuthority::SigningEntity do
9
- it "should behave as a signing entity" do
10
- @certificate.respond_to?(:is_signing_entity?).should be_true
11
- end
12
-
13
- it "should only be a signing entity if it's identified as a CA", :rfc3280 => true do
14
- @certificate.is_signing_entity?.should be_false
15
- @certificate.signing_entity = true
16
- @certificate.is_signing_entity?.should be_true
17
- end
18
-
19
- describe "Root certificates" do
20
- before(:each) do
21
- @certificate.signing_entity = true
22
- end
23
-
24
- it "should be able to be identified as a root certificate" do
25
- @certificate.is_root_entity?.should be_true
26
- end
27
-
28
- it "should only be a root certificate if the parent entity is itself", :rfc3280 => true do
29
- @certificate.parent.should == @certificate
30
- end
31
-
32
- it "should be a root certificate by default" do
33
- @certificate.is_root_entity?.should be_true
34
- end
35
-
36
- it "should be able to self-sign" do
37
- @certificate.serial_number.number = 1
38
- @certificate.subject.common_name = "chrischandler.name"
39
- @certificate.key_material.generate_key
40
- @certificate.sign!
41
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
42
- cert.subject.to_s.should == cert.issuer.to_s
43
- end
44
-
45
- it "should have the basicContraint CA:TRUE" do
46
- @certificate.serial_number.number = 1
47
- @certificate.subject.common_name = "chrischandler.name"
48
- @certificate.key_material.generate_key
49
- @certificate.sign!
50
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
51
- cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1].should == "CA:TRUE"
52
- end
53
- end
54
-
55
- describe "Intermediate certificates" do
56
- before(:each) do
57
- @different_cert = CertificateAuthority::Certificate.new
58
- @different_cert.signing_entity = true
59
- @different_cert.subject.common_name = "chrischandler.name root"
60
- @different_cert.key_material.generate_key
61
- @different_cert.serial_number.number = 2
62
- @different_cert.sign! #self-signed
63
- @certificate.parent = @different_cert
64
- @certificate.signing_entity = true
65
- end
66
-
67
- it "should be able to be identified as an intermediate certificate" do
68
- @certificate.is_intermediate_entity?.should be_true
69
- end
70
-
71
- it "should not be identified as a root" do
72
- @certificate.is_root_entity?.should be_false
73
- end
74
-
75
- it "should only be an intermediate certificate if the parent is a different entity" do
76
- @certificate.parent.should_not == @certificate
77
- @certificate.parent.should_not be_nil
78
- end
79
-
80
- it "should correctly be signed by a parent certificate" do
81
- @certificate.subject.common_name = "chrischandler.name"
82
- @certificate.key_material.generate_key
83
- @certificate.signing_entity = true
84
- @certificate.serial_number.number = 1
85
- @certificate.sign!
86
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
87
- cert.subject.to_s.should_not == cert.issuer.to_s
88
- end
89
-
90
- it "should have the basicContraint CA:TRUE" do
91
- @certificate.subject.common_name = "chrischandler.name"
92
- @certificate.key_material.generate_key
93
- @certificate.signing_entity = true
94
- @certificate.serial_number.number = 3
95
- @certificate.sign!
96
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
97
- # cert.extensions.first.value.should == "CA:TRUE"
98
- cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1].should == "CA:TRUE"
99
- end
100
-
101
- end
102
-
103
- describe "Terminal certificates" do
104
- before(:each) do
105
- @different_cert = CertificateAuthority::Certificate.new
106
- @different_cert.signing_entity = true
107
- @different_cert.subject.common_name = "chrischandler.name root"
108
- @different_cert.key_material.generate_key
109
- @different_cert.serial_number.number = 1
110
- @different_cert.sign! #self-signed
111
- @certificate.parent = @different_cert
112
- end
113
-
114
- it "should not be identified as an intermediate certificate" do
115
- @certificate.is_intermediate_entity?.should be_false
116
- end
117
-
118
- it "should not be identified as a root" do
119
- @certificate.is_root_entity?.should be_false
120
- end
121
-
122
- it "should have the basicContraint CA:FALSE" do
123
- @certificate.subject.common_name = "chrischandler.name"
124
- @certificate.key_material.generate_key
125
- @certificate.signing_entity = false
126
- @certificate.serial_number.number = 1
127
- @certificate.sign!
128
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
129
- # cert.extensions.first.value.should == "CA:FALSE"
130
- cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1].should == "CA:FALSE"
131
- end
132
- end
133
-
134
-
135
- it "should be able to be identified as a root certificate" do
136
- @certificate.respond_to?(:is_root_entity?).should be_true
137
- end
138
- end #End of SigningEntity
139
-
140
- describe "Signed certificates" do
141
- before(:each) do
142
- @certificate = CertificateAuthority::Certificate.new
143
- @certificate.subject.common_name = "chrischandler.name"
144
- @certificate.key_material.generate_key
145
- @certificate.serial_number.number = 1
146
- @certificate.sign!
147
- end
148
-
149
- it "should have a PEM encoded certificate body available" do
150
- @certificate.to_pem.should_not be_nil
151
- OpenSSL::X509::Certificate.new(@certificate.to_pem).should_not be_nil
152
- end
153
- end
154
-
155
- describe "X.509 V3 Extensions on Signed Certificates" do
156
- before(:each) do
157
- @certificate = CertificateAuthority::Certificate.new
158
- @certificate.subject.common_name = "chrischandler.name"
159
- @certificate.key_material.generate_key
160
- @certificate.serial_number.number = 1
161
- @signing_profile = {
162
- "extensions" => {
163
- "subjectAltName" => {"uris" => ["www.chrischandler.name"]},
164
- "certificatePolicies" => {
165
- "policy_identifier" => "1.3.5.7",
166
- "cps_uris" => ["http://my.host.name/", "http://my.your.name/"],
167
- "user_notice" => {
168
- "explicit_text" => "Testing!", "organization" => "RSpec Test organization name", "notice_numbers" => "1,2,3,4"
169
- }
170
- }
171
- }
172
- }
173
- @certificate.sign!(@signing_profile)
174
- end
175
-
176
- describe "SubjectAltName" do
177
- before(:each) do
178
- @certificate = CertificateAuthority::Certificate.new
179
- @certificate.subject.common_name = "chrischandler.name"
180
- @certificate.key_material.generate_key
181
- @certificate.serial_number.number = 1
182
- end
183
-
184
- it "should have a subjectAltName if specified" do
185
- @certificate.sign!({"extensions" => {"subjectAltName" => {"uris" => ["www.chrischandler.name"]}}})
186
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
187
- cert.extensions.map(&:oid).include?("subjectAltName").should be_true
188
- end
189
-
190
- it "should NOT have a subjectAltName if one was not specified" do
191
- @certificate.sign!
192
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
193
- cert.extensions.map(&:oid).include?("subjectAltName").should be_false
194
- end
195
- end
196
-
197
- describe "CertificatePolicies" do
198
- before(:each) do
199
- @certificate = CertificateAuthority::Certificate.new
200
- @certificate.subject.common_name = "chrischandler.name"
201
- @certificate.key_material.generate_key
202
- @certificate.serial_number.number = 1
203
- end
204
-
205
- it "should have a certificatePolicy if specified" do
206
- @certificate.sign!({
207
- "extensions" => {
208
- "certificatePolicies" => {
209
- "policy_identifier" => "1.3.5.7",
210
- "cps_uris" => ["http://my.host.name/", "http://my.your.name/"]
211
- }
212
- }
213
- })
214
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
215
- cert.extensions.map(&:oid).include?("certificatePolicies").should be_true
216
- end
217
-
218
- it "should contain a nested userNotice if specified" do
219
- pending
220
- # @certificate.sign!({
221
- # "extensions" => {
222
- # "certificatePolicies" => {
223
- # "policy_identifier" => "1.3.5.7",
224
- # "cps_uris" => ["http://my.host.name/", "http://my.your.name/"],
225
- # "user_notice" => {
226
- # "explicit_text" => "Testing!", "organization" => "RSpec Test organization name", "notice_numbers" => "1,2,3,4"
227
- # }
228
- # }
229
- # }
230
- # })
231
- # cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
232
- # cert.extensions.map(&:oid).include?("certificatePolicies").should be_true
233
- end
234
-
235
- it "should NOT include a certificatePolicy if not specified" do
236
- @certificate.sign!
237
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
238
- cert.extensions.map(&:oid).include?("certificatePolicies").should be_false
239
- end
240
- end
241
-
242
-
243
- it "should support BasicContraints" do
244
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
245
- cert.extensions.map(&:oid).include?("basicConstraints").should be_true
246
- end
247
-
248
- it "should support crlDistributionPoints" do
249
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
250
- cert.extensions.map(&:oid).include?("crlDistributionPoints").should be_true
251
- end
252
-
253
- it "should support subjectKeyIdentifier" do
254
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
255
- cert.extensions.map(&:oid).include?("subjectKeyIdentifier").should be_true
256
- end
257
-
258
- it "should support authorityKeyIdentifier" do
259
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
260
- cert.extensions.map(&:oid).include?("authorityKeyIdentifier").should be_true
261
- end
262
-
263
- it "should support authorityInfoAccess" do
264
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
265
- cert.extensions.map(&:oid).include?("authorityInfoAccess").should be_true
266
- end
267
-
268
- it "should support keyUsage" do
269
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
270
- cert.extensions.map(&:oid).include?("keyUsage").should be_true
271
- end
272
-
273
- it "should support extendedKeyUsage" do
274
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
275
- cert.extensions.map(&:oid).include?("extendedKeyUsage").should be_true
276
- end
277
- end
278
-
279
- describe "Signing profile" do
280
- before(:each) do
281
- @certificate = CertificateAuthority::Certificate.new
282
- @certificate.subject.common_name = "chrischandler.name"
283
- @certificate.key_material.generate_key
284
- @certificate.serial_number.number = 1
285
-
286
- @signing_profile = {
287
- "extensions" => {
288
- "basicConstraints" => {"ca" => false},
289
- "crlDistributionPoints" => {"uri" => "http://notme.com/other.crl" },
290
- "subjectKeyIdentifier" => {},
291
- "authorityKeyIdentifier" => {},
292
- "authorityInfoAccess" => {"ocsp" => ["http://youFillThisOut/ocsp/"] },
293
- "keyUsage" => {"usage" => ["digitalSignature","nonRepudiation"] },
294
- "extendedKeyUsage" => {"usage" => [ "serverAuth","clientAuth"]},
295
- "subjectAltName" => {"uris" => ["http://subdomains.youFillThisOut/"]},
296
- "certificatePolicies" => {
297
- "policy_identifier" => "1.3.5.8", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"], "user_notice" => {
298
- "explicit_text" => "Explicit Text Here", "organization" => "Organization name", "notice_numbers" => "1,2,3,4"
299
- }
300
- }
301
- }
302
- }
303
- end
304
-
305
- it "should be able to sign with an optional policy hash" do
306
- @certificate.sign!(@signing_profile)
307
- end
308
-
309
- end
310
-
311
-
312
- it "should have a distinguished name" do
313
- @certificate.distinguished_name.should_not be_nil
314
- end
315
-
316
- it "should have a serial number" do
317
- @certificate.serial_number.should_not be_nil
318
- end
319
-
320
- it "should have a subject" do
321
- @certificate.subject.should_not be_nil
322
- end
323
-
324
- it "should be able to have a parent entity" do
325
- @certificate.respond_to?(:parent).should be_true
326
- end
327
-
328
- it "should have key material" do
329
- @certificate.key_material.should_not be_nil
330
- end
331
-
332
- it "should have a not_before field" do
333
- @certificate.not_before.should_not be_nil
334
- end
335
-
336
- it "should have a not_after field" do
337
- @certificate.not_after.should_not be_nil
338
- end
339
-
340
- it "should default to one year validity" do
341
- @certificate.not_after.should < Time.now + 65 * 60 * 24 * 365 and
342
- @certificate.not_after.should > Time.now + 55 * 60 * 24 * 365
343
- end
344
-
345
- it "should be able to have a revoked at time" do
346
- @certificate.revoked?.should be_false
347
- @certificate.revoked_at = Time.now
348
- @certificate.revoked?.should be_true
349
- end
350
-
351
- end
@@ -1,38 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::DistinguishedName do
4
- before(:each) do
5
- @distinguished_name = CertificateAuthority::DistinguishedName.new
6
- end
7
-
8
- it "should provide the standard x.509 distinguished name common attributes" do
9
- @distinguished_name.respond_to?(:cn).should be_true
10
- @distinguished_name.respond_to?(:l).should be_true
11
- @distinguished_name.respond_to?(:s).should be_true
12
- @distinguished_name.respond_to?(:o).should be_true
13
- @distinguished_name.respond_to?(:ou).should be_true
14
- @distinguished_name.respond_to?(:c).should be_true
15
- end
16
-
17
- it "should provide human-readable equivalents to the distinguished name common attributes" do
18
- @distinguished_name.respond_to?(:common_name).should be_true
19
- @distinguished_name.respond_to?(:locality).should be_true
20
- @distinguished_name.respond_to?(:state).should be_true
21
- @distinguished_name.respond_to?(:organization).should be_true
22
- @distinguished_name.respond_to?(:organizational_unit).should be_true
23
- @distinguished_name.respond_to?(:country).should be_true
24
- end
25
-
26
- it "should require a common name" do
27
- @distinguished_name.valid?.should be_false
28
- @distinguished_name.errors.size.should == 1
29
- @distinguished_name.common_name = "chrischandler.name"
30
- @distinguished_name.valid?.should be_true
31
- end
32
-
33
- it "should be convertible to an OpenSSL::X509::Name" do
34
- @distinguished_name.common_name = "chrischandler.name"
35
- @distinguished_name.to_x509_name
36
- end
37
-
38
- end
@@ -1,53 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::Extensions do
4
- describe CertificateAuthority::Extensions::BasicContraints do
5
- it "should only allow true/false" do
6
- basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
7
- basic_constraints.valid?.should be_true
8
- basic_constraints.ca = "moo"
9
- basic_constraints.valid?.should be_false
10
- end
11
-
12
- it "should respond to :path_len" do
13
- basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
14
- basic_constraints.respond_to?(:path_len).should be_true
15
- end
16
-
17
- it "should raise an error if :path_len isn't a non-negative integer" do
18
- basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
19
- lambda {basic_constraints.path_len = "moo"}.should raise_error
20
- lambda {basic_constraints.path_len = -1}.should raise_error
21
- lambda {basic_constraints.path_len = 1.5}.should raise_error
22
- end
23
-
24
- it "should generate a proper OpenSSL extension string" do
25
- basic_constraints = CertificateAuthority::Extensions::BasicContraints.new
26
- basic_constraints.ca = true
27
- basic_constraints.path_len = 2
28
- basic_constraints.to_s.should == "CA:true,pathlen:2"
29
- end
30
- end
31
-
32
- describe CertificateAuthority::Extensions::SubjectAlternativeName do
33
- it "should respond to :uris" do
34
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
35
- subjectAltName.respond_to?(:uris).should be_true
36
- end
37
-
38
- it "should require 'uris' to be an Array" do
39
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
40
- lambda {subjectAltName.uris = "not an array"}.should raise_error
41
- end
42
-
43
- it "should generate a proper OpenSSL extension string" do
44
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
45
- subjectAltName.uris = ["http://localhost.altname.example.com"]
46
- subjectAltName.to_s.should == "URI:http://localhost.altname.example.com"
47
-
48
- subjectAltName.uris = ["http://localhost.altname.example.com", "http://other.example.com"]
49
- subjectAltName.to_s.should == "URI:http://localhost.altname.example.com,URI:http://other.example.com"
50
- end
51
-
52
- end
53
- end
@@ -1,96 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::MemoryKeyMaterial do
4
- before(:each) do
5
- @key_material = CertificateAuthority::MemoryKeyMaterial.new
6
- end
7
-
8
- it "should know if a key is in memory or hardware" do
9
- @key_material.is_in_hardware?.should_not be_nil
10
- @key_material.is_in_memory?.should_not be_nil
11
- end
12
-
13
- it "should use memory by default" do
14
- @key_material.is_in_memory?.should be_true
15
- end
16
-
17
- it "should be able to generate an RSA key" do
18
- @key_material.generate_key.should_not be_nil
19
- end
20
-
21
- it "should generate a proper OpenSSL::PKey::RSA" do
22
- @key_material.generate_key.class.should == OpenSSL::PKey::RSA
23
- end
24
-
25
- it "should be able to specify the size of the modulus to generate" do
26
- @key_material.generate_key(768).should_not be_nil
27
- end
28
-
29
- describe "in memory" do
30
- before(:all) do
31
- @key_material_in_memory = CertificateAuthority::MemoryKeyMaterial.new
32
- @key_material_in_memory.generate_key
33
- end
34
-
35
- it "should be able to retrieve the private key" do
36
- @key_material_in_memory.private_key.should_not be_nil
37
- end
38
-
39
- it "should be able to retrieve the public key" do
40
- @key_material_in_memory.public_key.should_not be_nil
41
- end
42
- end
43
-
44
- ## Anything that requires crypto hardware needs to be tagged as 'pkcs11'
45
- describe "in hardware", :pkcs11 => true do
46
- before(:each) do
47
- @key_material_in_hardware = CertificateAuthority::Pkcs11KeyMaterial.new
48
- @key_material_in_hardware.token_id = "46"
49
- @key_material_in_hardware.pkcs11_lib = "/usr/lib/libeTPkcs11.so"
50
- @key_material_in_hardware.openssl_pkcs11_engine_lib = "/usr/lib/engines/engine_pkcs11.so"
51
- @key_material_in_hardware.pin = "11111111"
52
- end
53
-
54
- it "should identify as being in hardware", :pkcs11 => true do
55
- @key_material_in_hardware.is_in_hardware?.should be_true
56
- end
57
-
58
- it "should return a Pkey ref if the private key is requested", :pkcs11 => true do
59
- @key_material_in_hardware.private_key.class.should == OpenSSL::PKey::RSA
60
- end
61
-
62
- it "should return a Pkey ref if the private key is requested", :pkcs11 => true do
63
- @key_material_in_hardware.public_key.class.should == OpenSSL::PKey::RSA
64
- end
65
-
66
- it "should accept an ID for on-token objects", :pkcs11 => true do
67
- @key_material_in_hardware.respond_to?(:token_id).should be_true
68
- end
69
-
70
- it "should accept a path to a shared library for a PKCS11 driver", :pkcs11 => true do
71
- @key_material_in_hardware.respond_to?(:pkcs11_lib).should be_true
72
- end
73
-
74
- it "should accept a path to OpenSSL's dynamic PKCS11 engine (provided by libengine-pkcs11-openssl)", :pkcs11 => true do
75
- @key_material_in_hardware.respond_to?(:openssl_pkcs11_engine_lib).should be_true
76
- end
77
-
78
- it "should accept an optional PIN to authenticate to the token", :pkcs11 => true do
79
- @key_material_in_hardware.respond_to?(:pin).should be_true
80
- end
81
-
82
- end
83
-
84
- it "not validate without public and private keys" do
85
- @key_material.valid?.should be_false
86
- @key_material.generate_key
87
- @key_material.valid?.should be_true
88
- pub = @key_material.public_key
89
- @key_material.public_key = nil
90
- @key_material.valid?.should be_false
91
- @key_material.public_key = pub
92
- @key_material.private_key = nil
93
- @key_material.valid?.should be_false
94
- end
95
-
96
- end