certificate_authority 0.1.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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