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,59 +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
- describe "from_openssl" do
39
- before do
40
- subject = "/CN=justincummins.name/L=on my laptop/ST=relaxed/C=as/O=programmer/OU=using this code"
41
- @name = OpenSSL::X509::Name.parse subject
42
- @dn = CertificateAuthority::DistinguishedName.from_openssl @name
43
- end
44
-
45
- it "should reject non Name objects" do
46
- lambda { CertificateAuthority::DistinguishedName.from_openssl "Not a OpenSSL::X509::Name" }.should raise_error
47
- end
48
-
49
- [:common_name, :locality, :state, :country, :organization, :organizational_unit].each do |field|
50
- it "should set the #{field} attribute" do
51
- @dn.send(field).should_not be_nil
52
- end
53
- end
54
-
55
- it "should create an equivalent object" do
56
- @dn.to_x509_name.to_s.split('/').should =~ @name.to_s.split('/')
57
- end
58
- end
59
- end
@@ -1,115 +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 for URIs" 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
-
53
- it "should respond to :dns_names" do
54
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
55
- subjectAltName.respond_to?(:dns_names).should be_true
56
- end
57
-
58
- it "should require 'dns_names' to be an Array" do
59
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
60
- lambda {subjectAltName.dns_names = "not an array"}.should raise_error
61
- end
62
-
63
- it "should generate a proper OpenSSL extension string for DNS names" do
64
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
65
- subjectAltName.dns_names = ["localhost.altname.example.com"]
66
- subjectAltName.to_s.should == "DNS:localhost.altname.example.com"
67
-
68
- subjectAltName.dns_names = ["localhost.altname.example.com", "other.example.com"]
69
- subjectAltName.to_s.should == "DNS:localhost.altname.example.com,DNS:other.example.com"
70
- end
71
-
72
- it "should respond to :ips" do
73
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
74
- subjectAltName.respond_to?(:ips).should be_true
75
- end
76
-
77
- it "should require 'ips' to be an Array" do
78
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
79
- lambda {subjectAltName.ips = "not an array"}.should raise_error
80
- end
81
-
82
- it "should generate a proper OpenSSL extension string for IPs" do
83
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
84
- subjectAltName.ips = ["1.2.3.4"]
85
- subjectAltName.to_s.should == "IP:1.2.3.4"
86
-
87
- subjectAltName.ips = ["1.2.3.4", "5.6.7.8"]
88
- subjectAltName.to_s.should == "IP:1.2.3.4,IP:5.6.7.8"
89
- end
90
-
91
- it "should generate a proper OpenSSL extension string for URIs IPs and DNS names together" do
92
- subjectAltName = CertificateAuthority::Extensions::SubjectAlternativeName.new
93
- subjectAltName.ips = ["1.2.3.4"]
94
- subjectAltName.to_s.should == "IP:1.2.3.4"
95
-
96
- subjectAltName.dns_names = ["localhost.altname.example.com"]
97
- subjectAltName.to_s.should == "DNS:localhost.altname.example.com,IP:1.2.3.4"
98
-
99
- subjectAltName.dns_names = ["localhost.altname.example.com", "other.example.com"]
100
- subjectAltName.to_s.should == "DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4"
101
-
102
- subjectAltName.ips = ["1.2.3.4", "5.6.7.8"]
103
- subjectAltName.to_s.should == "DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8"
104
-
105
- subjectAltName.uris = ["http://localhost.altname.example.com"]
106
- subjectAltName.to_s.should == "URI:http://localhost.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8"
107
-
108
- subjectAltName.uris = ["http://localhost.altname.example.com", "http://other.altname.example.com"]
109
- subjectAltName.to_s.should == "URI:http://localhost.altname.example.com,URI:http://other.altname.example.com,DNS:localhost.altname.example.com,DNS:other.example.com,IP:1.2.3.4,IP:5.6.7.8"
110
-
111
- end
112
-
113
-
114
- end
115
- end
@@ -1,100 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::KeyMaterial do
4
- [CertificateAuthority::MemoryKeyMaterial, CertificateAuthority::SigningRequestKeyMaterial].each do |key_material_class|
5
- before do
6
- @key_material = key_material_class.new
7
- end
8
-
9
- it "#{key_material_class} should know if a key is in memory or hardware" do
10
- @key_material.is_in_hardware?.should_not be_nil
11
- @key_material.is_in_memory?.should_not be_nil
12
- end
13
-
14
- it "should use memory by default" do
15
- @key_material.is_in_memory?.should be_true
16
- end
17
- end
18
- end
19
-
20
- describe CertificateAuthority::MemoryKeyMaterial do
21
- before(:each) do
22
- @key_material = CertificateAuthority::MemoryKeyMaterial.new
23
- end
24
-
25
- it "should be able to generate an RSA key" do
26
- @key_material.generate_key(1024).should_not be_nil
27
- end
28
-
29
- it "should generate a proper OpenSSL::PKey::RSA" do
30
- @key_material.generate_key(1024).class.should == OpenSSL::PKey::RSA
31
- end
32
-
33
- it "should be able to specify the size of the modulus to generate" do
34
- @key_material.generate_key(1024).should_not be_nil
35
- end
36
-
37
- describe "with generated key" do
38
- before(:all) do
39
- @key_material_in_memory = CertificateAuthority::MemoryKeyMaterial.new
40
- @key_material_in_memory.generate_key(1024)
41
- end
42
-
43
- it "should be able to retrieve the private key" do
44
- @key_material_in_memory.private_key.should_not be_nil
45
- end
46
-
47
- it "should be able to retrieve the public key" do
48
- @key_material_in_memory.public_key.should_not be_nil
49
- end
50
- end
51
-
52
- it "should not validate without public and private keys" do
53
- @key_material.valid?.should be_false
54
- @key_material.generate_key(1024)
55
- @key_material.valid?.should be_true
56
- pub = @key_material.public_key
57
- @key_material.public_key = nil
58
- @key_material.valid?.should be_false
59
- @key_material.public_key = pub
60
- @key_material.private_key = nil
61
- @key_material.valid?.should be_false
62
- end
63
- end
64
-
65
- describe CertificateAuthority::SigningRequestKeyMaterial do
66
- before(:each) do
67
- @request = OpenSSL::X509::Request.new <<CSR
68
- -----BEGIN CERTIFICATE REQUEST-----
69
- MIIBjTCB9wIBADBOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEU
70
- MBIGA1UEBxMLQmVyc2Vya2VsZXkxFDASBgNVBAoTC0NlcnRzICdSIFVzMIGfMA0G
71
- CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaGiBcv++581KYt6y2NNcUaZNPPeNZ0UkX
72
- ujzZQQllx7PlYmsKTE6ZzfTUc0AJvDBIuACg03eagaEaBZtUFbsLkSOLJyYiIfF5
73
- f9PuXImz2RDzBJQ/+u82gQAcvPhm94xK8jeNPcn0Ege7Y7SRK4YYonX+0ZveP02L
74
- FjuEfrZcZQIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAecOQz0RfnmSxxzOyHZ1e
75
- Wo2hQqPOmkfIbvL2l1Ml+HybJQJn6OpLmeveyU48SI2M7UqeNkHtsogMljy3re4L
76
- QlwK7lNd6SymdfSCPjUcdoLOaHolZXYNvCHltTc5skRHG7ti5yv4cu0ItIcCS0yp
77
- 7L3maDEbTLsDdouHeFfbLWA=
78
- -----END CERTIFICATE REQUEST-----
79
- CSR
80
- @key_material = CertificateAuthority::SigningRequestKeyMaterial.new @request
81
- end
82
-
83
- it "should generate from a CSR" do
84
- @key_material.should_not be_nil
85
- end
86
-
87
- it "should be able to expose a public key" do
88
- @key_material.public_key.should_not be_nil
89
- end
90
-
91
- it "should not have a private key" do
92
- @key_material.private_key.should be_nil
93
- end
94
-
95
- it "should raise when signature does not verify" do
96
- invalid = @request
97
- invalid.public_key = OpenSSL::PKey::RSA.new 512
98
- lambda { CertificateAuthority::SigningRequestKeyMaterial.new invalid }.should raise_error
99
- end
100
- end
@@ -1,104 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::OCSPHandler do
4
- before(:each) do
5
- @ocsp_handler = CertificateAuthority::OCSPHandler.new
6
-
7
- @root_certificate = CertificateAuthority::Certificate.new
8
- @root_certificate.signing_entity = true
9
- @root_certificate.subject.common_name = "OCSP 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://questionablesite.com"
17
- @certificate.parent = @root_certificate
18
- @certificate.serial_number.number = 2
19
- @certificate.sign!
20
-
21
- @ocsp_request = OpenSSL::OCSP::Request.new
22
- openssl_cert_issuer = OpenSSL::X509::Certificate.new(@root_certificate.to_pem)
23
- openssl_cert_subject = OpenSSL::X509::Certificate.new(@certificate.to_pem)
24
-
25
- cert_id = OpenSSL::OCSP::CertificateId.new(openssl_cert_subject, openssl_cert_issuer)
26
- @ocsp_request.add_certid(cert_id)
27
- @ocsp_handler.ocsp_request = @ocsp_request.to_der
28
- end
29
-
30
- it "should be able to accept an OCSP Request" do
31
- @ocsp_handler.ocsp_request = @ocsp_request
32
- @ocsp_handler.ocsp_request.should_not be_nil
33
- end
34
-
35
- it "should raise an error if you try and extract certificates without a raw request" do
36
- @ocsp_handler.extract_certificate_serials
37
- @ocsp_handler.ocsp_request = nil
38
- lambda {@ocsp_handler.extract_certificate_serials}.should raise_error
39
- end
40
-
41
- it "should return a hash of extracted certificates from OCSP requests" do
42
- result = @ocsp_handler.extract_certificate_serials
43
- result.size.should == 1
44
- end
45
-
46
- it "should be able to generate an OCSP response" do
47
- @ocsp_handler.extract_certificate_serials
48
- @ocsp_handler << @certificate
49
- @ocsp_handler.parent = @root_certificate
50
- @ocsp_handler.response
51
- end
52
-
53
- it "should require a 'parent' entity for signing" do
54
- @ocsp_handler.parent = @root_certificate
55
- @ocsp_handler.parent.should_not be_nil
56
- end
57
-
58
- it "should raise an error if you ask for the signed OCSP response without generating it" do
59
- @ocsp_handler.extract_certificate_serials
60
- @ocsp_handler << @certificate
61
- @ocsp_handler.parent = @root_certificate
62
- lambda { @ocsp_handler.to_der }.should raise_error
63
- @ocsp_handler.response
64
- @ocsp_handler.to_der.should_not be_nil
65
- end
66
-
67
- it "should raise an error if you generate a response without adding all certificates in request" do
68
- @ocsp_handler.extract_certificate_serials
69
- @ocsp_handler.parent = @root_certificate
70
- lambda { @ocsp_handler.response }.should raise_error
71
- end
72
-
73
- it "should raise an error if you generate a response without adding a parent signing entity" do
74
- @ocsp_handler.extract_certificate_serials
75
- @ocsp_handler << @certificate
76
- lambda { @ocsp_handler.response }.should raise_error
77
- end
78
-
79
- describe "Response" do
80
- before(:each) do
81
- @ocsp_handler.extract_certificate_serials
82
- @ocsp_handler << @certificate
83
- @ocsp_handler.parent = @root_certificate
84
- @ocsp_handler.response
85
-
86
- @openssl_ocsp_response = OpenSSL::OCSP::Response.new(@ocsp_handler.to_der)
87
- end
88
-
89
- it "should have a correct status/status string" do
90
- @openssl_ocsp_response.status_string.should == "successful"
91
- @openssl_ocsp_response.status.should == 0
92
- end
93
-
94
- it "should have an embedded BasicResponse with certificate statuses" do
95
- # [#<OpenSSL::OCSP::CertificateId:0x000001020ecad8>, 0, 1, nil, 2011-04-15 23:29:47 UTC, 2011-04-15 23:30:17 UTC, []]
96
- @openssl_ocsp_response.basic.status.first[1].should == 0 # Everything is OK
97
- end
98
-
99
- it "should have a next_update time" do
100
- @openssl_ocsp_response.basic.status.first[5].should_not be_nil
101
- @openssl_ocsp_response.basic.status.first[5].class.should == Time
102
- end
103
- end
104
- end
@@ -1,41 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- ## Anything that requires crypto hardware needs to be tagged as 'pkcs11'
4
- describe CertificateAuthority::Pkcs11KeyMaterial, :pkcs11 => true do
5
- before(:each) do
6
- @key_material_in_hardware = CertificateAuthority::Pkcs11KeyMaterial.new
7
- @key_material_in_hardware.token_id = "46"
8
- @key_material_in_hardware.pkcs11_lib = "/usr/lib/libeTPkcs11.so"
9
- @key_material_in_hardware.openssl_pkcs11_engine_lib = "/usr/lib/engines/engine_pkcs11.so"
10
- @key_material_in_hardware.pin = "11111111"
11
- end
12
-
13
- it "should identify as being in hardware", :pkcs11 => true do
14
- @key_material_in_hardware.is_in_hardware?.should be_true
15
- end
16
-
17
- it "should return a Pkey ref if the private key is requested", :pkcs11 => true do
18
- @key_material_in_hardware.private_key.class.should == OpenSSL::PKey::RSA
19
- end
20
-
21
- it "should return a Pkey ref if the public key is requested", :pkcs11 => true do
22
- @key_material_in_hardware.public_key.class.should == OpenSSL::PKey::RSA
23
- end
24
-
25
- it "should accept an ID for on-token objects", :pkcs11 => true do
26
- @key_material_in_hardware.respond_to?(:token_id).should be_true
27
- end
28
-
29
- it "should accept a path to a shared library for a PKCS11 driver", :pkcs11 => true do
30
- @key_material_in_hardware.respond_to?(:pkcs11_lib).should be_true
31
- end
32
-
33
- it "should accept a path to OpenSSL's dynamic PKCS11 engine (provided by libengine-pkcs11-openssl)", :pkcs11 => true do
34
- @key_material_in_hardware.respond_to?(:openssl_pkcs11_engine_lib).should be_true
35
- end
36
-
37
- it "should accept an optional PIN to authenticate to the token", :pkcs11 => true do
38
- @key_material_in_hardware.respond_to?(:pin).should be_true
39
- end
40
-
41
- end
@@ -1,20 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::SerialNumber do
4
- before(:each) do
5
- @serial_number = CertificateAuthority::SerialNumber.new
6
- end
7
-
8
- it "should support basic integer serial numbers", :rfc3280 => true do
9
- @serial_number.number = 25
10
- @serial_number.should be_valid
11
- @serial_number.number = "abc"
12
- @serial_number.should_not be_valid
13
- end
14
-
15
- it "should not allow negative serial numbers", :rfc3280 => true do
16
- @serial_number.number = -5
17
- @serial_number.should_not be_valid
18
- end
19
-
20
- end
@@ -1,4 +0,0 @@
1
- require File.dirname(__FILE__) + '/units_helper'
2
-
3
- describe CertificateAuthority::SigningEntity do
4
- end