certificate_authority 0.1.2 → 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 -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