certificate_authority_sonian 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/Gemfile.lock +39 -0
- data/README.rdoc +243 -0
- data/Rakefile +52 -0
- data/VERSION.yml +5 -0
- data/certificate_authority.gemspec +71 -0
- data/lib/certificate_authority.rb +19 -0
- data/lib/certificate_authority/certificate.rb +215 -0
- data/lib/certificate_authority/certificate_revocation_list.rb +59 -0
- data/lib/certificate_authority/distinguished_name.rb +58 -0
- data/lib/certificate_authority/extensions.rb +266 -0
- data/lib/certificate_authority/key_material.rb +135 -0
- data/lib/certificate_authority/ocsp_handler.rb +77 -0
- data/lib/certificate_authority/pkcs11_key_material.rb +65 -0
- data/lib/certificate_authority/serial_number.rb +9 -0
- data/lib/certificate_authority/signing_entity.rb +16 -0
- data/lib/tasks/certificate_authority.rake +23 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/units/certificate_authority_spec.rb +4 -0
- data/spec/units/certificate_revocation_list_spec.rb +68 -0
- data/spec/units/certificate_spec.rb +428 -0
- data/spec/units/distinguished_name_spec.rb +59 -0
- data/spec/units/extensions_spec.rb +115 -0
- data/spec/units/key_material_spec.rb +154 -0
- data/spec/units/ocsp_handler_spec.rb +104 -0
- data/spec/units/pkcs11_key_material_spec.rb +41 -0
- data/spec/units/serial_number_spec.rb +20 -0
- data/spec/units/signing_entity_spec.rb +4 -0
- data/spec/units/units_helper.rb +1 -0
- metadata +123 -0
@@ -0,0 +1,59 @@
|
|
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
|
@@ -0,0 +1,115 @@
|
|
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
|
@@ -0,0 +1,154 @@
|
|
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
|
+
common_request = <<CSR
|
81
|
+
-----BEGIN CERTIFICATE REQUEST-----
|
82
|
+
MIICzzCCAbcCAQAwgYkxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczESMBAG
|
83
|
+
A1UEBwwJQXJsaW5ndG9uMQwwCgYDVQQKDANCYWgxCzAJBgNVBAsMAk5vMRswGQYD
|
84
|
+
VQQDDBJwemVyby5ib3VneW1hbi5jb20xHjAcBgkqhkiG9w0BCQEWD3RqQHJ1Ynlp
|
85
|
+
c3RzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL+UD2trMNFh
|
86
|
+
lR3utCWBO3i9/VVdz13OnsSrmrnKvVeB6wRUd1gtxJ6sTi70ywN1vJqC3OuiO53G
|
87
|
+
CPzqWDsaVhQy7wTYCY+6uRfI23pcZZG/sGOHVJAJw+zadyd5LDqh3khsaZHBx1VK
|
88
|
+
ZiRee09QAnN4kK1uxB5B1ZCE01GzS9ERck7thwlcH2mDMuUtMxXmtEcl8sSeCkZO
|
89
|
+
CJ9TH21Q90oryZH14+fkhIjDTmyXAtj7kOjyAEu6aD+kYd03Yk+5XWJUVdpuug9Y
|
90
|
+
ZX7oMd8dzg9wiWzveKWypQ23BxcUS9ejiZVhj0TE0UPAKIhTw/0QkWoNcHOkwh29
|
91
|
+
X4fdAFIR3gkCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4IBAQAqlbx13HtfXFJf2boC
|
92
|
+
IcGrjaY+rOh9rNrXiGN9+DCZciQhEi8ZpOdygwZ4oKE/QjnPX+Q2sm/xjyhqp62v
|
93
|
+
NZsIGR8jnu1L7c5Jik72W/E2Jz6WLb/dWcGn45pSgi1HvRm+SXTWCIpZYfUrA+A/
|
94
|
+
x1x4CgU+tBFXtgMGTbK6F+JoaGsBeBkYG95pYurgS9mo62r0Mau3qi68DFzcXCuR
|
95
|
+
IibGCcF9hys7LbqLLDob6i1Y9TQtw5f6ARE4SkA9TvWM3VPDxn6F+Qfg8TAj7r1q
|
96
|
+
vecFC7r3d0ySWkdsy+Snzvt0ruu5pOfaTRBQqrIKVeGpOIp7sOhBlExtl/unHkCZ
|
97
|
+
IpZl
|
98
|
+
-----END CERTIFICATE REQUEST-----
|
99
|
+
CSR
|
100
|
+
@openssl_req = OpenSSL::X509::Request.new common_request
|
101
|
+
@certificate = CertificateAuthority::Certificate.new
|
102
|
+
@certificate.serial_number.number = 1
|
103
|
+
@certificate.subject.common_name = "chrischandler.name"
|
104
|
+
@certificate.key_material.generate_key(1024)
|
105
|
+
@certificate.sign!
|
106
|
+
@key_material = CertificateAuthority::SigningRequestKeyMaterial.new @request
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should generate from a CSR" do
|
110
|
+
@key_material.should_not be_nil
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should be able to expose a public key" do
|
114
|
+
@key_material.public_key.should_not be_nil
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should not have a private key" do
|
118
|
+
@key_material.private_key.should be_nil
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should raise when signature does not verify" do
|
122
|
+
invalid = @request
|
123
|
+
invalid.public_key = OpenSSL::PKey::RSA.new 512
|
124
|
+
lambda { CertificateAuthority::SigningRequestKeyMaterial.new invalid }.should raise_error
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should only accept valid OpenSSL requests" do
|
128
|
+
lambda { CertificateAuthority::SigningRequestKeyMaterial.new OpenSSL::X509::Request.new }.should raise_error(OpenSSL::X509::RequestError)
|
129
|
+
req = CertificateAuthority::SigningRequestKeyMaterial.new @openssl_req
|
130
|
+
req.csr.subject.should_not be_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should add a cert when signed by a root ca" do
|
134
|
+
csr = CertificateAuthority::SigningRequestKeyMaterial.new @openssl_req
|
135
|
+
signed_cert = csr.sign_and_certify(@certificate, @certificate.key_material.private_key, 555)
|
136
|
+
signed_cert.should_not be_nil
|
137
|
+
signed_cert.subject.to_x509_name.to_s.should == "/CN=pzero.bougyman.com/O=Bah/OU=No/ST=Texas/L=Arlington/C=US"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "can sign an SPKAC SPKI and generate a certificate" do
|
141
|
+
spkac_raw = <<SPKAC
|
142
|
+
MIICQDCCASgwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDEqzj21++aMWvN6zzwDXKKpR9g5hIeAPYqbUdaPFePhtz7R73l7fVxDeQZDQpQxTqts0/w0wEa/A1ehHCtAkDoTYzjwX8G0Gkb90poA156I8b4Cl1Q2veKbLsaOsMWItlXSU6HULQ5McfYfvEaPmIKiIr0UIFdzMDcy9TnY854w9TcQVvLJZcQkaM3dy/p9W4gg0a9hBJwwFUUR2UV/nEEi+++HbsOE46Z7Y3qoQhLrL4DNUrXUPDVeqac1SmNfKTA71QADbezWDfKi9habHHGXqk18i2Pl6uA2mpNPuSWnEHbQONgnfeoWZBvMWkwlolaBeWhGSmgcL/HqaRLlFAgMBAAEWADANBgkqhkiG9w0BAQQFAAOCAQEAp8wOvrl2QG9p1PS19dnrh4l0JWNAPB+d1kc64xUG6FAfGCKnOHzdDndTJfEERhWqFA0XL+mvKXCQsYKXkOuxYYmxJXZsdcCj7mOMhI2uMrEVd1ALFmG5WBW1Mo3nHHa/BX24fAOLv0+aGXYTz3oaMFydBw+XPZ26x9pO9LjlQQYGGyQRMpceWfej367KnR4a7IafDGBUI6OoWsx/7kQRIGkbmzi+dnU7HgpEExyz+uuxlUxrZqa13Ys8dBp62NBJWFanPl+AhMe8g/Li5aiERrMUbFtiXzOp48Si7J54fs+U3w0WoD9cdG5mN2Rn8Jog8azl+YC/XY987YGAi7Y8EQ==
|
143
|
+
SPKAC
|
144
|
+
spkac = OpenSSL::Netscape::SPKI.new spkac_raw
|
145
|
+
dn = CertificateAuthority::DistinguishedName.new
|
146
|
+
dn.common_name = "chrischandler.name"
|
147
|
+
csr = CertificateAuthority::SigningRequestKeyMaterial.new spkac
|
148
|
+
lambda { csr.sign_and_certify(@certificate, @certificate.key_material.private_key, 554) }.should raise_error(RuntimeError)
|
149
|
+
signed_cert = csr.sign_and_certify(@certificate, @certificate.key_material.private_key, 555, :dn => dn)
|
150
|
+
signed_cert.should_not be_nil
|
151
|
+
signed_cert.subject.to_x509_name.to_s.should == "/CN=chrischandler.name"
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,104 @@
|
|
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
|
@@ -0,0 +1,41 @@
|
|
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
|