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,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