certificate_authority 0.1.6 → 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 -31
  7. data/README.rdoc +91 -2
  8. data/Rakefile +6 -41
  9. data/certificate_authority.gemspec +22 -66
  10. data/lib/certificate_authority.rb +6 -5
  11. data/lib/certificate_authority/certificate.rb +91 -36
  12. data/lib/certificate_authority/certificate_revocation_list.rb +34 -14
  13. data/lib/certificate_authority/core_extensions.rb +46 -0
  14. data/lib/certificate_authority/distinguished_name.rb +64 -16
  15. data/lib/certificate_authority/extensions.rb +417 -45
  16. data/lib/certificate_authority/key_material.rb +30 -9
  17. data/lib/certificate_authority/ocsp_handler.rb +75 -5
  18. data/lib/certificate_authority/pkcs11_key_material.rb +0 -2
  19. data/lib/certificate_authority/revocable.rb +14 -0
  20. data/lib/certificate_authority/serial_number.rb +15 -2
  21. data/lib/certificate_authority/signing_request.rb +91 -0
  22. data/lib/certificate_authority/validations.rb +31 -0
  23. data/lib/certificate_authority/version.rb +3 -0
  24. metadata +76 -48
  25. data/VERSION.yml +0 -5
  26. data/spec/spec_helper.rb +0 -4
  27. data/spec/units/certificate_authority_spec.rb +0 -4
  28. data/spec/units/certificate_revocation_list_spec.rb +0 -68
  29. data/spec/units/certificate_spec.rb +0 -428
  30. data/spec/units/distinguished_name_spec.rb +0 -59
  31. data/spec/units/extensions_spec.rb +0 -115
  32. data/spec/units/key_material_spec.rb +0 -100
  33. data/spec/units/ocsp_handler_spec.rb +0 -104
  34. data/spec/units/pkcs11_key_material_spec.rb +0 -41
  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,5 +0,0 @@
1
- ---
2
- :major: 0
3
- :minor: 1
4
- :patch: 6
5
- :build:
@@ -1,4 +0,0 @@
1
- require 'rubygems'
2
- require 'rspec'
3
-
4
- require File.dirname(__FILE__) + '/../lib/certificate_authority'
@@ -1,4 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority do
4
- end
@@ -1,68 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::CertificateRevocationList do
4
- before(:each) do
5
- @crl = CertificateAuthority::CertificateRevocationList.new
6
-
7
- @root_certificate = CertificateAuthority::Certificate.new
8
- @root_certificate.signing_entity = true
9
- @root_certificate.subject.common_name = "CRL Root"
10
- @root_certificate.key_material.generate_key(1024)
11
- @root_certificate.serial_number.number = 1
12
- @root_certificate.sign!
13
-
14
- @certificate = CertificateAuthority::Certificate.new
15
- @certificate.key_material.generate_key(1024)
16
- @certificate.subject.common_name = "http://bogusSite.com"
17
- @certificate.parent = @root_certificate
18
- @certificate.serial_number.number = 2
19
- @certificate.sign!
20
-
21
- @crl.parent = @root_certificate
22
- @certificate.revoked_at = Time.now
23
- end
24
-
25
- it "should accept a list of certificates" do
26
- @crl << @certificate
27
- end
28
-
29
- it "should complain if you add a certificate without a revocation time" do
30
- @certificate.revoked_at = nil
31
- lambda{ @crl << @certificate}.should raise_error
32
- end
33
-
34
- it "should have a 'parent' that will be responsible for signing" do
35
- @crl.parent = @root_certificate
36
- @crl.parent.should_not be_nil
37
- end
38
-
39
- it "should raise an error if you try and sign a CRL without attaching a parent" do
40
- @crl.parent = nil
41
- lambda { @crl.sign! }.should raise_error
42
- end
43
-
44
- it "should be able to generate a proper CRL" do
45
- @crl << @certificate
46
- lambda {@crl.to_pem}.should raise_error
47
- @crl.parent = @root_certificate
48
- @crl.sign!
49
- @crl.to_pem.should_not be_nil
50
- OpenSSL::X509::CRL.new(@crl.to_pem).should_not be_nil
51
- end
52
-
53
- describe "Next update" do
54
- it "should be able to set a 'next_update' value" do
55
- @crl.next_update = (60 * 60 * 10) # 10 Hours
56
- @crl.next_update.should_not be_nil
57
- end
58
-
59
- it "should throw an error if we try and sign up with a negative next_update" do
60
- @crl.sign!
61
- @crl.next_update = - (60 * 60 * 10)
62
- lambda{@crl.sign!}.should raise_error
63
- end
64
-
65
- end
66
-
67
-
68
- end
@@ -1,428 +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(1024)
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(1024)
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(1024)
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(1024)
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(1024)
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.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1].should == "CA:TRUE"
98
- end
99
-
100
- end
101
-
102
- describe "Terminal certificates" do
103
- before(:each) do
104
- @different_cert = CertificateAuthority::Certificate.new
105
- @different_cert.signing_entity = true
106
- @different_cert.subject.common_name = "chrischandler.name root"
107
- @different_cert.key_material.generate_key(1024)
108
- @different_cert.serial_number.number = 1
109
- @different_cert.sign! #self-signed
110
- @certificate.parent = @different_cert
111
- end
112
-
113
- it "should not be identified as an intermediate certificate" do
114
- @certificate.is_intermediate_entity?.should be_false
115
- end
116
-
117
- it "should not be identified as a root" do
118
- @certificate.is_root_entity?.should be_false
119
- end
120
-
121
- it "should have the basicContraint CA:FALSE" do
122
- @certificate.subject.common_name = "chrischandler.name"
123
- @certificate.key_material.generate_key(1024)
124
- @certificate.signing_entity = false
125
- @certificate.serial_number.number = 1
126
- @certificate.sign!
127
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
128
- cert.extensions.map{|i| [i.oid,i.value] }.select{|i| i.first == "basicConstraints"}.first[1].should == "CA:FALSE"
129
- end
130
- end
131
-
132
-
133
- it "should be able to be identified as a root certificate" do
134
- @certificate.respond_to?(:is_root_entity?).should be_true
135
- end
136
- end #End of SigningEntity
137
-
138
- describe "Signed certificates" do
139
- before(:each) do
140
- @certificate = CertificateAuthority::Certificate.new
141
- @certificate.subject.common_name = "chrischandler.name"
142
- @certificate.key_material.generate_key(1024)
143
- @certificate.serial_number.number = 1
144
- @certificate.sign!
145
- end
146
-
147
- it "should have a PEM encoded certificate body available" do
148
- @certificate.to_pem.should_not be_nil
149
- OpenSSL::X509::Certificate.new(@certificate.to_pem).should_not be_nil
150
- end
151
- end
152
-
153
- describe "X.509 V3 Extensions on Signed Certificates" do
154
- before(:each) do
155
- @certificate = CertificateAuthority::Certificate.new
156
- @certificate.subject.common_name = "chrischandler.name"
157
- @certificate.key_material.generate_key(1024)
158
- @certificate.serial_number.number = 1
159
- @signing_profile = {
160
- "extensions" => {
161
- "subjectAltName" => {"uris" => ["www.chrischandler.name"]},
162
- "certificatePolicies" => {
163
- "policy_identifier" => "1.3.5.7",
164
- "cps_uris" => ["http://my.host.name/", "http://my.your.name/"],
165
- "user_notice" => {
166
- "explicit_text" => "Testing!", "organization" => "RSpec Test organization name", "notice_numbers" => "1,2,3,4"
167
- }
168
- }
169
- }
170
- }
171
- @certificate.sign!(@signing_profile)
172
- end
173
-
174
- describe "SubjectAltName" do
175
- before(:each) do
176
- @certificate = CertificateAuthority::Certificate.new
177
- @certificate.subject.common_name = "chrischandler.name"
178
- @certificate.key_material.generate_key(1024)
179
- @certificate.serial_number.number = 1
180
- end
181
-
182
- it "should have a subjectAltName if specified" do
183
- @certificate.sign!({"extensions" => {"subjectAltName" => {"uris" => ["www.chrischandler.name"]}}})
184
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
185
- cert.extensions.map(&:oid).include?("subjectAltName").should be_true
186
- end
187
-
188
- it "should NOT have a subjectAltName if one was not specified" do
189
- @certificate.sign!
190
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
191
- cert.extensions.map(&:oid).include?("subjectAltName").should be_false
192
- end
193
- end
194
-
195
- describe "AuthorityInfoAccess" do
196
- before(:each) do
197
- @certificate = CertificateAuthority::Certificate.new
198
- @certificate.subject.common_name = "chrischandler.name"
199
- @certificate.key_material.generate_key(1024)
200
- @certificate.serial_number.number = 1
201
- end
202
-
203
- it "should have an authority info access if specified" do
204
- @certificate.sign!({"extensions" => {"authorityInfoAccess" => {"ocsp" => ["www.chrischandler.name"]}}})
205
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
206
- cert.extensions.map(&:oid).include?("authorityInfoAccess").should be_true
207
- end
208
- end
209
-
210
- describe "CrlDistributionPoints" do
211
- before(:each) do
212
- @certificate = CertificateAuthority::Certificate.new
213
- @certificate.subject.common_name = "chrischandler.name"
214
- @certificate.key_material.generate_key(1024)
215
- @certificate.serial_number.number = 1
216
- end
217
-
218
- it "should have a crlDistributionPoint if specified" do
219
- @certificate.sign!({"extensions" => {"crlDistributionPoints" => {"uri" => ["http://crlThingy.com"]}}})
220
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
221
- cert.extensions.map(&:oid).include?("crlDistributionPoints").should be_true
222
- end
223
-
224
- it "should NOT have a crlDistributionPoint if one was not specified" do
225
- @certificate.sign!
226
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
227
- cert.extensions.map(&:oid).include?("crlDistributionPoints").should be_false
228
- end
229
- end
230
-
231
-
232
- describe "CertificatePolicies" do
233
- before(:each) do
234
- @certificate = CertificateAuthority::Certificate.new
235
- @certificate.subject.common_name = "chrischandler.name"
236
- @certificate.key_material.generate_key(1024)
237
- @certificate.serial_number.number = 1
238
- end
239
-
240
- it "should have a certificatePolicy if specified" do
241
- @certificate.sign!({
242
- "extensions" => {
243
- "certificatePolicies" => {
244
- "policy_identifier" => "1.3.5.7",
245
- "cps_uris" => ["http://my.host.name/", "http://my.your.name/"]
246
- }
247
- }
248
- })
249
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
250
- cert.extensions.map(&:oid).include?("certificatePolicies").should be_true
251
- end
252
-
253
- it "should contain a nested userNotice if specified" do
254
- pending
255
- # @certificate.sign!({
256
- # "extensions" => {
257
- # "certificatePolicies" => {
258
- # "policy_identifier" => "1.3.5.7",
259
- # "cps_uris" => ["http://my.host.name/", "http://my.your.name/"],
260
- # "user_notice" => {
261
- # "explicit_text" => "Testing!", "organization" => "RSpec Test organization name", "notice_numbers" => "1,2,3,4"
262
- # }
263
- # }
264
- # }
265
- # })
266
- # cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
267
- # cert.extensions.map(&:oid).include?("certificatePolicies").should be_true
268
- end
269
-
270
- it "should NOT include a certificatePolicy if not specified" do
271
- @certificate.sign!
272
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
273
- cert.extensions.map(&:oid).include?("certificatePolicies").should be_false
274
- end
275
- end
276
-
277
-
278
- it "should support BasicContraints" do
279
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
280
- cert.extensions.map(&:oid).include?("basicConstraints").should be_true
281
- end
282
-
283
- it "should support subjectKeyIdentifier" do
284
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
285
- cert.extensions.map(&:oid).include?("subjectKeyIdentifier").should be_true
286
- end
287
-
288
- it "should support authorityKeyIdentifier" do
289
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
290
- cert.extensions.map(&:oid).include?("authorityKeyIdentifier").should be_true
291
- end
292
-
293
- it "should order subjectKeyIdentifier before authorityKeyIdentifier" do
294
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
295
- cert.extensions.map(&:oid).select do |oid|
296
- ["subjectKeyIdentifier", "authorityKeyIdentifier"].include?(oid)
297
- end.should == ["subjectKeyIdentifier", "authorityKeyIdentifier"]
298
- end
299
-
300
- it "should support keyUsage" do
301
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
302
- cert.extensions.map(&:oid).include?("keyUsage").should be_true
303
- end
304
-
305
- it "should support extendedKeyUsage" do
306
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
307
- cert.extensions.map(&:oid).include?("extendedKeyUsage").should be_true
308
- end
309
- end
310
-
311
- describe "Signing profile" do
312
- before(:each) do
313
- @certificate = CertificateAuthority::Certificate.new
314
- @certificate.subject.common_name = "chrischandler.name"
315
- @certificate.key_material.generate_key(1024)
316
- @certificate.serial_number.number = 1
317
-
318
- @signing_profile = {
319
- "extensions" => {
320
- "basicConstraints" => {"ca" => false},
321
- "crlDistributionPoints" => {"uri" => "http://notme.com/other.crl" },
322
- "subjectKeyIdentifier" => {},
323
- "authorityKeyIdentifier" => {},
324
- "authorityInfoAccess" => {"ocsp" => ["http://youFillThisOut/ocsp/"] },
325
- "keyUsage" => {"usage" => ["digitalSignature","nonRepudiation"] },
326
- "extendedKeyUsage" => {"usage" => [ "serverAuth","clientAuth"]},
327
- "subjectAltName" => {"uris" => ["http://subdomains.youFillThisOut/"]},
328
- "certificatePolicies" => {
329
- "policy_identifier" => "1.3.5.8", "cps_uris" => ["http://my.host.name/", "http://my.your.name/"], "user_notice" => {
330
- "explicit_text" => "Explicit Text Here", "organization" => "Organization name", "notice_numbers" => "1,2,3,4"
331
- }
332
- }
333
- }
334
- }
335
- end
336
-
337
- it "should be able to sign with an optional policy hash" do
338
- @certificate.sign!(@signing_profile)
339
- end
340
-
341
- it "should support a default signing digest of SHA512" do
342
- @certificate.sign!(@signing_profile)
343
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
344
- cert.signature_algorithm.should == "sha512WithRSAEncryption"
345
- end
346
-
347
- it "should support a configurable digest algorithm" do
348
- @signing_profile.merge!({"digest" => "SHA1"})
349
- @certificate.sign!(@signing_profile)
350
- cert = OpenSSL::X509::Certificate.new(@certificate.to_pem)
351
- cert.signature_algorithm.should == "sha1WithRSAEncryption"
352
- end
353
-
354
- end
355
-
356
- describe "from_openssl" do
357
- before(:each) do
358
- @pem_cert=<<CERT
359
- -----BEGIN CERTIFICATE-----
360
- MIICFDCCAc6gAwIBAgIJAPDLgMilKuayMA0GCSqGSIb3DQEBBQUAMEgxCzAJBgNV
361
- BAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMQowCAYDVQQKEwEgMRgwFgYDVQQD
362
- Ew9WZXJ5IFNtYWxsIENlcnQwHhcNMTIwNTAzMDMyODI1WhcNMTMwNTAzMDMyODI1
363
- WjBIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEKMAgGA1UEChMB
364
- IDEYMBYGA1UEAxMPVmVyeSBTbWFsbCBDZXJ0MEwwDQYJKoZIhvcNAQEBBQADOwAw
365
- OAIxAN6+33+WQ3FBMt+vMhshxOj+8W7V64pDKCJ3pVlnSn36imBWqrN0AGWX8qjv
366
- S+GzGwIDAQABo4GqMIGnMB0GA1UdDgQWBBRMUQ/HpPrAkKOufS5h+xPtEuzyWDB4
367
- BgNVHSMEcTBvgBRMUQ/HpPrAkKOufS5h+xPtEuzyWKFMpEowSDELMAkGA1UEBhMC
368
- VVMxEzARBgNVBAgTClNvbWUtU3RhdGUxCjAIBgNVBAoTASAxGDAWBgNVBAMTD1Zl
369
- cnkgU21hbGwgQ2VydIIJAPDLgMilKuayMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
370
- AQEFBQADMQAq0CsqEChn4uf6MkXYBwaAAmS3JLmagyliJe5zM3y8dZz6Em2Ugb8o
371
- 1cCaKaHJHSg=
372
- -----END CERTIFICATE-----
373
- CERT
374
- @openssl_cert = OpenSSL::X509::Certificate.new @pem_cert
375
- @small_cert = CertificateAuthority::Certificate.from_openssl @openssl_cert
376
- end
377
-
378
- it "should reject non-Certificate arguments" do
379
- lambda { CertificateAuthority::Certificate.from_openssl "a string" }.should raise_error
380
- end
381
-
382
- it "should only be missing a private key" do
383
- @small_cert.should_not be_valid
384
- @small_cert.key_material.private_key = "data"
385
- @small_cert.should be_valid
386
- end
387
- end
388
-
389
- it "should have a distinguished name" do
390
- @certificate.distinguished_name.should_not be_nil
391
- end
392
-
393
- it "should have a serial number" do
394
- @certificate.serial_number.should_not be_nil
395
- end
396
-
397
- it "should have a subject" do
398
- @certificate.subject.should_not be_nil
399
- end
400
-
401
- it "should be able to have a parent entity" do
402
- @certificate.respond_to?(:parent).should be_true
403
- end
404
-
405
- it "should have key material" do
406
- @certificate.key_material.should_not be_nil
407
- end
408
-
409
- it "should have a not_before field" do
410
- @certificate.not_before.should_not be_nil
411
- end
412
-
413
- it "should have a not_after field" do
414
- @certificate.not_after.should_not be_nil
415
- end
416
-
417
- it "should default to one year validity" do
418
- @certificate.not_after.should < Time.now + 65 * 60 * 24 * 365 and
419
- @certificate.not_after.should > Time.now + 55 * 60 * 24 * 365
420
- end
421
-
422
- it "should be able to have a revoked at time" do
423
- @certificate.revoked?.should be_false
424
- @certificate.revoked_at = Time.now
425
- @certificate.revoked?.should be_true
426
- end
427
-
428
- end