r509 0.8.1 → 0.9
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.
- data/README.md +343 -151
- data/Rakefile +26 -23
- data/bin/r509 +126 -112
- data/bin/r509-parse +24 -24
- data/doc/R509.html +169 -7
- data/doc/R509/ASN1.html +370 -0
- data/doc/R509/ASN1/GeneralName.html +1121 -0
- data/doc/R509/ASN1/GeneralNames.html +843 -0
- data/doc/R509/ASN1/NoticeReference.html +392 -0
- data/doc/R509/ASN1/PolicyInformation.html +387 -0
- data/doc/R509/ASN1/PolicyQualifiers.html +455 -0
- data/doc/R509/ASN1/UserNotice.html +386 -0
- data/doc/R509/{Crl.html → CRL.html} +7 -7
- data/doc/R509/CRL/Administrator.html +1559 -0
- data/doc/R509/{Crl/Parser.html → CRL/SignedList.html} +501 -210
- data/doc/R509/{Csr.html → CSR.html} +444 -314
- data/doc/R509/Cert.html +866 -617
- data/doc/R509/Cert/Extensions.html +52 -41
- data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +70 -35
- data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +387 -4
- data/doc/R509/Cert/Extensions/BasicConstraints.html +61 -25
- data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +354 -0
- data/doc/R509/Cert/Extensions/CertificatePolicies.html +340 -0
- data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +440 -49
- data/doc/R509/Cert/Extensions/{CrlDistributionPoints.html → InhibitAnyPolicy.html} +52 -35
- data/doc/R509/Cert/Extensions/KeyUsage.html +247 -121
- data/doc/R509/Cert/Extensions/NameConstraints.html +445 -0
- data/doc/R509/Cert/Extensions/OCSPNoCheck.html +239 -0
- data/doc/R509/Cert/Extensions/PolicyConstraints.html +424 -0
- data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +437 -62
- data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +52 -10
- data/doc/R509/CertificateAuthority.html +4 -4
- data/doc/R509/CertificateAuthority/Signer.html +154 -187
- data/doc/R509/Config.html +6 -6
- data/doc/R509/Config/{CaConfig.html → CAConfig.html} +451 -348
- data/doc/R509/Config/{CaConfigPool.html → CAConfigPool.html} +47 -47
- data/doc/R509/Config/CAProfile.html +1015 -0
- data/doc/R509/Config/SubjectItemPolicy.html +86 -86
- data/doc/R509/IOHelpers.html +22 -22
- data/doc/R509/MessageDigest.html +14 -14
- data/doc/R509/NameSanitizer.html +53 -53
- data/doc/R509/{Ocsp.html → OCSP.html} +9 -9
- data/doc/R509/{Ocsp → OCSP}/Request.html +7 -7
- data/doc/R509/{Ocsp → OCSP}/Request/Nonce.html +56 -11
- data/doc/R509/{Ocsp → OCSP}/Response.html +44 -44
- data/doc/R509/{OidMapper.html → OIDMapper.html} +23 -39
- data/doc/R509/PrivateKey.html +415 -168
- data/doc/R509/R509Error.html +3 -3
- data/doc/R509/{Spki.html → SPKI.html} +354 -192
- data/doc/R509/Subject.html +224 -113
- data/doc/R509/Validity.html +27 -5
- data/doc/R509/Validity/Checker.html +13 -13
- data/doc/R509/Validity/DefaultChecker.html +13 -13
- data/doc/R509/Validity/DefaultWriter.html +14 -14
- data/doc/R509/Validity/Status.html +39 -39
- data/doc/R509/Validity/Writer.html +18 -18
- data/doc/_index.html +138 -35
- data/doc/class_list.html +1 -1
- data/doc/css/style.css +10 -0
- data/doc/file.README.html +368 -171
- data/doc/file.r509.html +92 -69
- data/doc/frames.html +1 -1
- data/doc/index.html +368 -171
- data/doc/method_list.html +910 -390
- data/doc/top-level-namespace.html +3 -3
- data/lib/r509.rb +32 -16
- data/lib/r509/asn1.rb +375 -0
- data/lib/r509/cert.rb +381 -364
- data/lib/r509/cert/extensions.rb +443 -76
- data/lib/r509/certificate_authority.rb +407 -0
- data/lib/r509/config.rb +547 -351
- data/lib/r509/crl.rb +336 -366
- data/lib/r509/csr.rb +278 -289
- data/lib/r509/ec-hack.rb +37 -0
- data/lib/r509/exceptions.rb +3 -3
- data/lib/r509/io_helpers.rb +44 -44
- data/lib/r509/message_digest.rb +53 -0
- data/lib/r509/ocsp.rb +80 -70
- data/lib/r509/oid_mapper.rb +32 -0
- data/lib/r509/private_key.rb +228 -0
- data/lib/r509/spki.rb +145 -93
- data/lib/r509/subject.rb +203 -110
- data/lib/r509/validity.rb +70 -68
- data/lib/r509/version.rb +2 -2
- data/r509.yaml +92 -69
- data/spec/asn1_spec.rb +402 -0
- data/spec/cert/extensions_spec.rb +957 -494
- data/spec/cert_spec.rb +382 -307
- data/spec/certificate_authority_spec.rb +668 -250
- data/spec/config_spec.rb +515 -302
- data/spec/crl_spec.rb +197 -198
- data/spec/csr_spec.rb +334 -289
- data/spec/fixtures.rb +247 -171
- data/spec/fixtures/cert1.der +0 -0
- data/spec/fixtures/cert1.pem +0 -0
- data/spec/fixtures/cert1_public_key_modulus.txt +0 -0
- data/spec/fixtures/cert3.p12 +0 -0
- data/spec/fixtures/cert3.pem +0 -0
- data/spec/fixtures/cert3_key.pem +0 -0
- data/spec/fixtures/cert3_key_des3.pem +0 -0
- data/spec/fixtures/cert4.pem +0 -0
- data/spec/fixtures/cert5.pem +0 -0
- data/spec/fixtures/cert6.pem +0 -0
- data/spec/fixtures/cert_expired.pem +0 -0
- data/spec/fixtures/cert_inhibit.pem +24 -0
- data/spec/fixtures/cert_name_constraints.pem +29 -0
- data/spec/fixtures/cert_not_yet_valid.pem +0 -0
- data/spec/fixtures/cert_ocsp_no_check.pem +18 -0
- data/spec/fixtures/cert_policy_constraints.pem +31 -0
- data/spec/fixtures/cert_san.pem +0 -0
- data/spec/fixtures/cert_san2.pem +0 -0
- data/spec/fixtures/cert_unknown_extension.pem +28 -0
- data/spec/fixtures/config_pool_test_minimal.yaml +11 -11
- data/spec/fixtures/config_test.yaml +54 -36
- data/spec/fixtures/config_test_dsa.yaml +35 -0
- data/spec/fixtures/config_test_ec.yaml +35 -0
- data/spec/fixtures/config_test_engine_key.yaml +5 -5
- data/spec/fixtures/config_test_engine_no_key_name.yaml +4 -4
- data/spec/fixtures/config_test_minimal.yaml +4 -4
- data/spec/fixtures/config_test_password.yaml +5 -5
- data/spec/fixtures/config_test_various.yaml +111 -74
- data/spec/fixtures/crl_list_file.txt +0 -0
- data/spec/fixtures/crl_with_reason.pem +0 -0
- data/spec/fixtures/csr1.der +0 -0
- data/spec/fixtures/csr1.pem +0 -0
- data/spec/fixtures/csr1_key.der +0 -0
- data/spec/fixtures/csr1_key.pem +0 -0
- data/spec/fixtures/csr1_key_encrypted_des3.pem +0 -0
- data/spec/fixtures/csr1_newlines.pem +0 -0
- data/spec/fixtures/csr1_no_begin_end.pem +0 -0
- data/spec/fixtures/csr1_public_key_modulus.txt +0 -0
- data/spec/fixtures/csr2.pem +0 -0
- data/spec/fixtures/csr2_key.pem +0 -0
- data/spec/fixtures/csr3.pem +0 -0
- data/spec/fixtures/csr4.pem +0 -0
- data/spec/fixtures/csr_dsa.pem +0 -0
- data/spec/fixtures/csr_invalid_signature.pem +0 -0
- data/spec/fixtures/dsa_key.pem +0 -0
- data/spec/fixtures/dsa_root.cer +28 -0
- data/spec/fixtures/dsa_root.key +20 -0
- data/spec/fixtures/ec_csr2.der +0 -0
- data/spec/fixtures/ec_csr2.pem +8 -0
- data/spec/fixtures/ec_key1.der +0 -0
- data/spec/fixtures/ec_key1.pem +6 -0
- data/spec/fixtures/ec_key1_encrypted.pem +9 -0
- data/spec/fixtures/ec_key2.pem +6 -0
- data/spec/fixtures/hmacsha1.sig +1 -0
- data/spec/fixtures/hmacsha512.sig +1 -0
- data/spec/fixtures/key4.pem +0 -0
- data/spec/fixtures/key4_encrypted_des3.pem +0 -0
- data/spec/fixtures/missing_key_identifier_ca.cer +0 -0
- data/spec/fixtures/missing_key_identifier_ca.key +0 -0
- data/spec/fixtures/ocsptest.r509.local.pem +0 -0
- data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
- data/spec/fixtures/ocsptest2.r509.local.pem +0 -0
- data/spec/fixtures/second_ca.cer +0 -0
- data/spec/fixtures/second_ca.key +0 -0
- data/spec/fixtures/spkac.der +0 -0
- data/spec/fixtures/spkac.txt +0 -0
- data/spec/fixtures/spkac_dsa.txt +1 -1
- data/spec/fixtures/spkac_dsa_no_verify.txt +1 -0
- data/spec/fixtures/spkac_ec.txt +1 -0
- data/spec/fixtures/spkac_rsa_newlines.txt +13 -0
- data/spec/fixtures/stca.pem +0 -0
- data/spec/fixtures/stca_ocsp_request.der +0 -0
- data/spec/fixtures/stca_ocsp_response.der +0 -0
- data/spec/fixtures/test1.csr +0 -0
- data/spec/fixtures/test_ca.cer +0 -0
- data/spec/fixtures/test_ca.key +0 -0
- data/spec/fixtures/test_ca.p12 +0 -0
- data/spec/fixtures/test_ca_des3.key +0 -0
- data/spec/fixtures/test_ca_ec.cer +14 -0
- data/spec/fixtures/test_ca_ec.key +6 -0
- data/spec/fixtures/test_ca_ec_ee.cer +22 -0
- data/spec/fixtures/test_ca_ec_ee.key +6 -0
- data/spec/fixtures/test_ca_ocsp.cer +0 -0
- data/spec/fixtures/test_ca_ocsp.key +0 -0
- data/spec/fixtures/test_ca_ocsp.p12 +0 -0
- data/spec/fixtures/test_ca_ocsp_chain.txt +0 -0
- data/spec/fixtures/test_ca_ocsp_response.der +0 -0
- data/spec/fixtures/test_ca_subroot.cer +0 -0
- data/spec/fixtures/test_ca_subroot.key +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp.cer +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp.key +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
- data/spec/fixtures/unknown_oid.csr +0 -0
- data/spec/message_digest_spec.rb +104 -84
- data/spec/ocsp_spec.rb +105 -105
- data/spec/oid_mapper_spec.rb +21 -21
- data/spec/private_key_spec.rb +275 -0
- data/spec/r509_spec.rb +35 -0
- data/spec/spec_helper.rb +15 -6
- data/spec/spki_spec.rb +221 -142
- data/spec/subject_spec.rb +232 -164
- data/spec/validity_spec.rb +91 -91
- metadata +79 -25
- data/doc/R509/Config/CaProfile.html +0 -651
- data/doc/R509/Crl/Administrator.html +0 -2073
- data/lib/r509/certificateauthority.rb +0 -290
- data/lib/r509/messagedigest.rb +0 -49
- data/lib/r509/oidmapper.rb +0 -32
- data/lib/r509/privatekey.rb +0 -185
- data/spec/privatekey_spec.rb +0 -198
data/spec/config_spec.rb
CHANGED
@@ -2,348 +2,561 @@ require 'spec_helper'
|
|
2
2
|
require 'r509/config'
|
3
3
|
require 'r509/exceptions'
|
4
4
|
|
5
|
-
describe R509::Config::
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
pool["first"].should == nil
|
11
|
-
end
|
12
|
-
|
13
|
-
it "has one config" do
|
14
|
-
config = R509::Config::CaConfig.new(
|
15
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
16
|
-
:profiles => { "first_profile" => R509::Config::CaProfile.new }
|
17
|
-
)
|
18
|
-
|
19
|
-
pool = R509::Config::CaConfigPool.new({
|
20
|
-
"first" => config
|
21
|
-
})
|
22
|
-
|
23
|
-
pool["first"].should == config
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
context "all configs" do
|
28
|
-
it "no configs" do
|
29
|
-
pool = R509::Config::CaConfigPool.new({})
|
30
|
-
pool.all.should == []
|
31
|
-
end
|
32
|
-
|
33
|
-
it "one config" do
|
34
|
-
config = R509::Config::CaConfig.new(
|
35
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
36
|
-
:profiles => { "first_profile" => R509::Config::CaProfile.new }
|
37
|
-
)
|
38
|
-
|
39
|
-
pool = R509::Config::CaConfigPool.new({
|
40
|
-
"first" => config
|
41
|
-
})
|
42
|
-
|
43
|
-
pool.all.should == [config]
|
44
|
-
end
|
45
|
-
|
46
|
-
it "two configs" do
|
47
|
-
config1 = R509::Config::CaConfig.new(
|
48
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
49
|
-
:profiles => { "first_profile" => R509::Config::CaProfile.new }
|
50
|
-
)
|
51
|
-
config2 = R509::Config::CaConfig.new(
|
52
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
53
|
-
:profiles => { "first_profile" => R509::Config::CaProfile.new }
|
54
|
-
)
|
55
|
-
|
56
|
-
pool = R509::Config::CaConfigPool.new({
|
57
|
-
"first" => config1,
|
58
|
-
"second" => config2
|
59
|
-
})
|
60
|
-
|
61
|
-
pool.all.size.should == 2
|
62
|
-
pool.all.include?(config1).should == true
|
63
|
-
pool.all.include?(config2).should == true
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "loaded from YAML" do
|
68
|
-
it "should load two configs" do
|
69
|
-
pool = R509::Config::CaConfigPool.from_yaml("certificate_authorities", File.read("#{File.dirname(__FILE__)}/fixtures/config_pool_test_minimal.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
70
|
-
|
71
|
-
pool.names.should include("test_ca", "second_ca")
|
72
|
-
|
73
|
-
pool["test_ca"].should_not == nil
|
74
|
-
pool["test_ca"].num_profiles.should == 0
|
75
|
-
pool["second_ca"].should_not == nil
|
76
|
-
pool["second_ca"].num_profiles.should == 0
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
5
|
+
describe R509::Config::CAConfigPool do
|
6
|
+
context "defined manually" do
|
7
|
+
it "has no configs" do
|
8
|
+
pool = R509::Config::CAConfigPool.new({})
|
80
9
|
|
81
|
-
|
82
|
-
before :each do
|
83
|
-
@config = R509::Config::CaConfig.new(
|
84
|
-
:ca_cert => TestFixtures.test_ca_cert
|
85
|
-
)
|
10
|
+
pool["first"].should == nil
|
86
11
|
end
|
87
12
|
|
88
|
-
|
13
|
+
it "has one config" do
|
14
|
+
config = R509::Config::CAConfig.new(
|
15
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
16
|
+
:profiles => { "first_profile" => R509::Config::CAProfile.new }
|
17
|
+
)
|
89
18
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
its(:ocsp_start_skew_seconds) {should == 3600}
|
94
|
-
its(:cdp_location) {should be_nil}
|
95
|
-
its(:ocsp_location) {should be_nil}
|
96
|
-
its(:num_profiles) {should == 0}
|
19
|
+
pool = R509::Config::CAConfigPool.new({
|
20
|
+
"first" => config
|
21
|
+
})
|
97
22
|
|
98
|
-
|
99
|
-
@config.ca_cert.to_pem.should == TestFixtures.test_ca_cert.to_pem
|
23
|
+
pool["first"].should == config
|
100
24
|
end
|
25
|
+
end
|
101
26
|
|
102
|
-
|
103
|
-
|
27
|
+
context "all configs" do
|
28
|
+
it "no configs" do
|
29
|
+
pool = R509::Config::CAConfigPool.new({})
|
30
|
+
pool.all.should == []
|
104
31
|
end
|
105
32
|
|
106
|
-
it "
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
112
|
-
it "loads the config even if :ca_cert does not contain a private key" do
|
113
|
-
config = R509::Config::CaConfig.new( :ca_cert => R509::Cert.new( :cert => TestFixtures::TEST_CA_CERT) )
|
114
|
-
config.ca_cert.subject.to_s.should_not be_nil
|
115
|
-
end
|
116
|
-
it "raises an error if :ocsp_cert that is not R509::Cert" do
|
117
|
-
expect { R509::Config::CaConfig.new(:ca_cert => TestFixtures.test_ca_cert, :ocsp_cert => "not a cert") }.to raise_error ArgumentError, ':ocsp_cert, if provided, must be of type R509::Cert'
|
118
|
-
end
|
119
|
-
it "raises an error if :ocsp_cert does not contain a private key" do
|
120
|
-
expect { R509::Config::CaConfig.new( :ca_cert => TestFixtures.test_ca_cert, :ocsp_cert => R509::Cert.new( :cert => TestFixtures::TEST_CA_CERT) ) }.to raise_error ArgumentError, ':ocsp_cert must contain a private key, not just a certificate'
|
121
|
-
end
|
122
|
-
it "returns the correct cert object on #ocsp_cert if none is specified" do
|
123
|
-
@config.ocsp_cert.should == @config.ca_cert
|
124
|
-
end
|
125
|
-
it "returns the correct cert object on #ocsp_cert if an ocsp_cert was specified" do
|
126
|
-
ocsp_cert = R509::Cert.new(
|
127
|
-
:cert => TestFixtures::TEST_CA_OCSP_CERT,
|
128
|
-
:key => TestFixtures::TEST_CA_OCSP_KEY
|
129
|
-
)
|
130
|
-
config = R509::Config::CaConfig.new(
|
131
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
132
|
-
:ocsp_cert => ocsp_cert
|
133
|
-
)
|
33
|
+
it "one config" do
|
34
|
+
config = R509::Config::CAConfig.new(
|
35
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
36
|
+
:profiles => { "first_profile" => R509::Config::CAProfile.new }
|
37
|
+
)
|
134
38
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
config = R509::Config::CaConfig.new(
|
139
|
-
:ca_cert => TestFixtures.test_ca_cert
|
140
|
-
)
|
39
|
+
pool = R509::Config::CAConfigPool.new({
|
40
|
+
"first" => config
|
41
|
+
})
|
141
42
|
|
142
|
-
|
43
|
+
pool.all.should == [config]
|
143
44
|
end
|
144
45
|
|
145
|
-
it "
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
46
|
+
it "two configs" do
|
47
|
+
config1 = R509::Config::CAConfig.new(
|
48
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
49
|
+
:profiles => { "first_profile" => R509::Config::CAProfile.new }
|
50
|
+
)
|
51
|
+
config2 = R509::Config::CAConfig.new(
|
52
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
53
|
+
:profiles => { "first_profile" => R509::Config::CAProfile.new }
|
54
|
+
)
|
151
55
|
|
152
|
-
|
153
|
-
|
56
|
+
pool = R509::Config::CAConfigPool.new({
|
57
|
+
"first" => config1,
|
58
|
+
"second" => config2
|
59
|
+
})
|
154
60
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
)
|
159
|
-
|
160
|
-
config.profile("first_profile").should == first_profile
|
61
|
+
pool.all.size.should == 2
|
62
|
+
pool.all.include?(config1).should == true
|
63
|
+
pool.all.include?(config2).should == true
|
161
64
|
end
|
65
|
+
end
|
162
66
|
|
163
|
-
|
164
|
-
|
67
|
+
context "loaded from YAML" do
|
68
|
+
it "should load two configs" do
|
69
|
+
pool = R509::Config::CAConfigPool.from_yaml("certificate_authorities", File.read("#{File.dirname(__FILE__)}/fixtures/config_pool_test_minimal.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
165
70
|
|
166
|
-
|
167
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
168
|
-
:profiles => { "first_profile" => first_profile }
|
169
|
-
)
|
71
|
+
pool.names.should include("test_ca", "second_ca")
|
170
72
|
|
171
|
-
|
73
|
+
pool["test_ca"].should_not == nil
|
74
|
+
pool["test_ca"].num_profiles.should == 0
|
75
|
+
pool["second_ca"].should_not == nil
|
76
|
+
pool["second_ca"].num_profiles.should == 0
|
172
77
|
end
|
78
|
+
end
|
79
|
+
end
|
173
80
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
end
|
181
|
-
it "loads OCSP cert/key from yaml" do
|
182
|
-
config = R509::Config::CaConfig.from_yaml("ocsp_delegate_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
183
|
-
config.ocsp_cert.has_private_key?.should == true
|
184
|
-
config.ocsp_cert.subject.to_s.should == "/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 OCSP Signer"
|
185
|
-
end
|
186
|
-
it "loads OCSP pkcs12 from yaml" do
|
187
|
-
config = R509::Config::CaConfig.from_yaml("ocsp_pkcs12_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
188
|
-
config.ocsp_cert.has_private_key?.should == true
|
189
|
-
config.ocsp_cert.subject.to_s.should == "/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 OCSP Signer"
|
190
|
-
end
|
191
|
-
it "loads OCSP cert/key in engine from yaml" do
|
192
|
-
#most of this code path is tested by loading ca_cert engine.
|
193
|
-
#look there for the extensive doubling
|
194
|
-
expect { R509::Config::CaConfig.from_yaml("ocsp_engine_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(R509::R509Error,"You must supply a key_name with an engine")
|
195
|
-
end
|
196
|
-
it "loads OCSP chain from yaml" do
|
197
|
-
config = R509::Config::CaConfig.from_yaml("ocsp_chain_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
198
|
-
config.ocsp_chain.size.should == 2
|
199
|
-
config.ocsp_chain[0].kind_of?(OpenSSL::X509::Certificate).should == true
|
200
|
-
config.ocsp_chain[1].kind_of?(OpenSSL::X509::Certificate).should == true
|
201
|
-
end
|
202
|
-
it "should load subject_item_policy from yaml (if present)" do
|
203
|
-
config = R509::Config::CaConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
204
|
-
config.profile("server").subject_item_policy.should be_nil
|
205
|
-
config.profile("server_with_subject_item_policy").subject_item_policy.optional.should include("O","OU")
|
206
|
-
config.profile("server_with_subject_item_policy").subject_item_policy.required.should include("CN","ST","C")
|
207
|
-
end
|
81
|
+
describe R509::Config::CAConfig do
|
82
|
+
before :each do
|
83
|
+
@config = R509::Config::CAConfig.new(
|
84
|
+
:ca_cert => TestFixtures.test_ca_cert
|
85
|
+
)
|
86
|
+
end
|
208
87
|
|
209
|
-
|
210
|
-
config = R509::Config::CaConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_minimal.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
211
|
-
config.num_profiles.should == 0
|
212
|
-
end
|
88
|
+
subject {@config}
|
213
89
|
|
214
|
-
|
215
|
-
|
216
|
-
|
90
|
+
its(:message_digest) {should == "SHA1"}
|
91
|
+
its(:crl_validity_hours) {should == 168}
|
92
|
+
its(:ocsp_validity_hours) {should == 168}
|
93
|
+
its(:ocsp_start_skew_seconds) {should == 3600}
|
94
|
+
its(:cdp_location) {should be_nil}
|
95
|
+
its(:ocsp_location) {should be_nil}
|
96
|
+
its(:num_profiles) {should == 0}
|
217
97
|
|
218
|
-
|
219
|
-
|
220
|
-
|
98
|
+
it "should have the proper CA cert" do
|
99
|
+
@config.ca_cert.to_pem.should == TestFixtures.test_ca_cert.to_pem
|
100
|
+
end
|
221
101
|
|
222
|
-
|
223
|
-
|
224
|
-
|
102
|
+
it "should have the proper CA key" do
|
103
|
+
@config.ca_cert.key.to_pem.should == TestFixtures.test_ca_cert.key.to_pem
|
104
|
+
end
|
225
105
|
|
226
|
-
|
227
|
-
|
106
|
+
context "validates data" do
|
107
|
+
it "raises an error if you don't pass :ca_cert" do
|
108
|
+
expect { R509::Config::CAConfig.new(:crl_validity_hours => 2) }.to raise_error ArgumentError, 'Config object requires that you pass :ca_cert'
|
228
109
|
end
|
229
|
-
|
230
|
-
|
231
|
-
expect { R509::Config::CaConfig.from_yaml("pkcs12_engine_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(R509::R509Error, "You can't specify both engine and pkcs12")
|
110
|
+
it "raises an error if :ca_cert is not of type R509::Cert" do
|
111
|
+
expect { R509::Config::CAConfig.new(:ca_cert => 'not a cert, and not right type') }.to raise_error ArgumentError, ':ca_cert must be of type R509::Cert'
|
232
112
|
end
|
233
|
-
|
234
|
-
|
235
|
-
config = R509::Config::CaConfig.from_yaml("cert_no_key_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
236
|
-
config.ca_cert.subject.to_s.should_not be_nil
|
113
|
+
it "raises an error if :ocsp_cert that is not R509::Cert" do
|
114
|
+
expect { R509::Config::CAConfig.new(:ca_cert => TestFixtures.test_ca_cert, :ocsp_cert => "not a cert") }.to raise_error ArgumentError, ':ocsp_cert, if provided, must be of type R509::Cert'
|
237
115
|
end
|
116
|
+
it "raises an error if :ocsp_cert does not contain a private key" do
|
117
|
+
expect { R509::Config::CAConfig.new( :ca_cert => TestFixtures.test_ca_cert, :ocsp_cert => R509::Cert.new( :cert => TestFixtures::TEST_CA_CERT) ) }.to raise_error ArgumentError, ':ocsp_cert must contain a private key, not just a certificate'
|
118
|
+
end
|
119
|
+
it "raises an error if you pass an ocsp_location that is not an array" do
|
120
|
+
expect { R509::Config::CAConfig.new( :ca_cert => TestFixtures.test_ca_cert, :ocsp_location => "some-url" ) }.to raise_error(ArgumentError, 'ocsp_location must be an array if provided')
|
121
|
+
end
|
122
|
+
it "raises an error if you pass a ca_issuers_location that is not an array" do
|
123
|
+
expect { R509::Config::CAConfig.new( :ca_cert => TestFixtures.test_ca_cert, :ca_issuers_location => "some-url" ) }.to raise_error(ArgumentError, 'ca_issuers_location must be an array if provided')
|
124
|
+
end
|
125
|
+
it "raises an error if you pass a cdp_location that is not an array" do
|
126
|
+
expect { R509::Config::CAConfig.new( :ca_cert => TestFixtures.test_ca_cert, :cdp_location => "some-url" ) }.to raise_error(ArgumentError, 'cdp_location must be an array if provided')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
it "loads the config even if :ca_cert does not contain a private key" do
|
130
|
+
config = R509::Config::CAConfig.new( :ca_cert => R509::Cert.new( :cert => TestFixtures::TEST_CA_CERT) )
|
131
|
+
config.ca_cert.subject.to_s.should_not be_nil
|
132
|
+
end
|
133
|
+
it "returns the correct cert object on #ocsp_cert if none is specified" do
|
134
|
+
@config.ocsp_cert.should == @config.ca_cert
|
135
|
+
end
|
136
|
+
it "returns the correct cert object on #ocsp_cert if an ocsp_cert was specified" do
|
137
|
+
ocsp_cert = R509::Cert.new(
|
138
|
+
:cert => TestFixtures::TEST_CA_OCSP_CERT,
|
139
|
+
:key => TestFixtures::TEST_CA_OCSP_KEY
|
140
|
+
)
|
141
|
+
config = R509::Config::CAConfig.new(
|
142
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
143
|
+
:ocsp_cert => ocsp_cert
|
144
|
+
)
|
145
|
+
|
146
|
+
config.ocsp_cert.should == ocsp_cert
|
147
|
+
end
|
148
|
+
it "fails to specify a non-Config::CAProfile as the profile" do
|
149
|
+
config = R509::Config::CAConfig.new(
|
150
|
+
:ca_cert => TestFixtures.test_ca_cert
|
151
|
+
)
|
152
|
+
|
153
|
+
expect{ config.set_profile("bogus", "not a Config::CAProfile")}.to raise_error TypeError
|
154
|
+
end
|
155
|
+
|
156
|
+
it "shouldn't let you specify a profile that's not a Config::CAProfile, on instantiation" do
|
157
|
+
expect{ R509::Config::CAConfig.new(
|
158
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
159
|
+
:profiles => { "first_profile" => "not a Config::CAProfile" }
|
160
|
+
) }.to raise_error TypeError
|
161
|
+
end
|
162
|
+
|
163
|
+
it "can specify a single profile" do
|
164
|
+
first_profile = R509::Config::CAProfile.new
|
165
|
+
|
166
|
+
config = R509::Config::CAConfig.new(
|
167
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
168
|
+
:profiles => { "first_profile" => first_profile }
|
169
|
+
)
|
170
|
+
|
171
|
+
config.profile("first_profile").should == first_profile
|
172
|
+
end
|
173
|
+
|
174
|
+
it "raises an error if you specify an invalid profile" do
|
175
|
+
first_profile = R509::Config::CAProfile.new
|
176
|
+
|
177
|
+
config = R509::Config::CAConfig.new(
|
178
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
179
|
+
:profiles => { "first_profile" => first_profile }
|
180
|
+
)
|
181
|
+
|
182
|
+
expect { config.profile("non-existent-profile") }.to raise_error(R509::R509Error, "unknown profile 'non-existent-profile'")
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should load YAML" do
|
186
|
+
config = R509::Config::CAConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
187
|
+
config.crl_validity_hours.should == 72
|
188
|
+
config.ocsp_validity_hours.should == 96
|
189
|
+
config.message_digest.should == "SHA1"
|
190
|
+
config.num_profiles.should == 7
|
191
|
+
config.profile("ocsp_delegate_with_no_check").ocsp_no_check.should == true
|
192
|
+
config.profile("inhibit_policy").inhibit_any_policy.should == 2
|
193
|
+
config.profile("policy_constraints").policy_constraints["require_explicit_policy"].should == 1
|
194
|
+
config.profile("policy_constraints").policy_constraints["inhibit_policy_mapping"].should == 0
|
195
|
+
config.profile("name_constraints").name_constraints.should_not be_nil
|
196
|
+
end
|
197
|
+
it "loads OCSP cert/key from yaml" do
|
198
|
+
config = R509::Config::CAConfig.from_yaml("ocsp_delegate_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
199
|
+
config.ocsp_cert.has_private_key?.should == true
|
200
|
+
config.ocsp_cert.subject.to_s.should == "/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 OCSP Signer"
|
201
|
+
end
|
202
|
+
it "loads OCSP pkcs12 from yaml" do
|
203
|
+
config = R509::Config::CAConfig.from_yaml("ocsp_pkcs12_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
204
|
+
config.ocsp_cert.has_private_key?.should == true
|
205
|
+
config.ocsp_cert.subject.to_s.should == "/C=US/ST=Illinois/L=Chicago/O=r509 LLC/CN=r509 OCSP Signer"
|
206
|
+
end
|
207
|
+
it "loads OCSP cert/key in engine from yaml" do
|
208
|
+
#most of this code path is tested by loading ca_cert engine.
|
209
|
+
#look there for the extensive doubling
|
210
|
+
expect { R509::Config::CAConfig.from_yaml("ocsp_engine_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError,"You must supply a key_name with an engine")
|
211
|
+
end
|
212
|
+
it "loads OCSP chain from yaml" do
|
213
|
+
config = R509::Config::CAConfig.from_yaml("ocsp_chain_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
214
|
+
config.ocsp_chain.size.should == 2
|
215
|
+
config.ocsp_chain[0].kind_of?(OpenSSL::X509::Certificate).should == true
|
216
|
+
config.ocsp_chain[1].kind_of?(OpenSSL::X509::Certificate).should == true
|
217
|
+
end
|
218
|
+
it "should load subject_item_policy from yaml (if present)" do
|
219
|
+
config = R509::Config::CAConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
220
|
+
config.profile("server").subject_item_policy.should be_nil
|
221
|
+
config.profile("server_with_subject_item_policy").subject_item_policy.optional.should include("O","OU")
|
222
|
+
config.profile("server_with_subject_item_policy").subject_item_policy.required.should include("CN","ST","C")
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should load YAML which only has a CA Cert and Key defined" do
|
226
|
+
config = R509::Config::CAConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_minimal.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
227
|
+
config.num_profiles.should == 0
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should load YAML which has CA cert and key with password" do
|
231
|
+
expect { R509::Config::CAConfig.from_yaml("password_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_password.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to_not raise_error
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should load YAML which has a PKCS12 with password" do
|
235
|
+
expect { R509::Config::CAConfig.from_yaml("pkcs12_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to_not raise_error
|
236
|
+
end
|
237
|
+
|
238
|
+
it "raises error on YAML with pkcs12 and key" do
|
239
|
+
expect { R509::Config::CAConfig.from_yaml("pkcs12_key_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError, "You can't specify both pkcs12 and key")
|
240
|
+
end
|
241
|
+
|
242
|
+
it "raises error on YAML with pkcs12 and cert" do
|
243
|
+
expect { R509::Config::CAConfig.from_yaml("pkcs12_cert_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError, "You can't specify both pkcs12 and cert")
|
244
|
+
end
|
245
|
+
|
246
|
+
it "raises error on YAML with pkcs12 and engine" do
|
247
|
+
expect { R509::Config::CAConfig.from_yaml("pkcs12_engine_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError, "You can't specify both engine and pkcs12")
|
248
|
+
end
|
249
|
+
|
250
|
+
it "loads config with cert and no key (useful in certain cases)" do
|
251
|
+
config = R509::Config::CAConfig.from_yaml("cert_no_key_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
252
|
+
config.ca_cert.subject.to_s.should_not be_nil
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should load YAML which has an engine" do
|
256
|
+
#i can test this, it's just gonna take a whole lot of floorin' it!
|
257
|
+
conf = double("conf")
|
258
|
+
engine = double("engine")
|
259
|
+
faux_key = double("faux_key")
|
260
|
+
|
261
|
+
public_key = OpenSSL::PKey::RSA.new("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqLfP8948QEZMMkZNHLDP\nOHZVrPdVvecD6bp8dz96LalMQiWjgMkJf7mPHXoNMQ5rIpntiUOmhupu+sty30+C\ndbZZIbZohioTSYq9ZIC/LC9ME12F78GRMKhBGA+ZiouzWvXqOEdMnanfSgKrlSIS\nssF71dfmOEQ08fn9Vl5jAgWmGe+v615iHqBNGr64kYooTrZYLaPlTScO1UZ76vnB\nHfNQU+tsEZNXxtZXKQqkxHxLShCOj6qmYRNn/upTZoWWd04+zXjYGEC3eKvi9ctN\n9FY+KJ6QCCa8H0Kt3cU5qyw6pzdljhbG6NKhod7OMqlGjmHdsCAYAqe3xH+V/8oe\ndwIDAQAB\n-----END PUBLIC KEY-----\n")
|
262
|
+
|
263
|
+
conf.should_receive(:kind_of?).with(Hash).and_return(true)
|
264
|
+
conf.should_receive(:[]).with("ca_cert").and_return(
|
265
|
+
"cert" => "#{File.dirname(__FILE__)}/fixtures/test_ca.cer",
|
266
|
+
"engine" => engine,
|
267
|
+
"key_name" => "r509_key"
|
268
|
+
)
|
269
|
+
conf.should_receive(:[]).at_least(1).times.and_return(nil)
|
270
|
+
conf.should_receive(:has_key?).at_least(1).times.and_return(false)
|
271
|
+
|
272
|
+
engine.should_receive(:respond_to?).with(:load_private_key).and_return(true)
|
273
|
+
engine.should_receive(:kind_of?).with(OpenSSL::Engine).and_return(true)
|
274
|
+
faux_key.should_receive(:public_key).and_return(public_key)
|
275
|
+
engine.should_receive(:load_private_key).twice.with("r509_key").and_return(faux_key)
|
276
|
+
|
277
|
+
config = R509::Config::CAConfig.load_from_hash(conf)
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should fail if YAML for ca_cert contains engine and key" do
|
281
|
+
expect { R509::Config::CAConfig.from_yaml("engine_and_key", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_engine_key.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError, "You can't specify both key and engine")
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should fail if YAML for ca_cert contains engine but no key_name" do
|
285
|
+
expect { R509::Config::CAConfig.from_yaml("engine_no_key_name", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_engine_no_key_name.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError, 'You must supply a key_name with an engine')
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should fail if YAML config is null" do
|
289
|
+
expect{ R509::Config::CAConfig.from_yaml("no_config_here", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError)
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should fail if YAML config isn't a hash" do
|
293
|
+
expect{ R509::Config::CAConfig.from_yaml("config_is_string", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError)
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should fail if YAML config doesn't give a root CA directory that's a directory" do
|
297
|
+
expect{ R509::Config::CAConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures/no_directory_here"}) }.to raise_error(R509::R509Error)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should load YAML from filename" do
|
301
|
+
config = R509::Config::CAConfig.load_yaml("test_ca", "#{File.dirname(__FILE__)}/fixtures/config_test.yaml", {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
302
|
+
config.crl_validity_hours.should == 72
|
303
|
+
config.ocsp_validity_hours.should == 96
|
304
|
+
config.message_digest.should == "SHA1"
|
305
|
+
end
|
306
|
+
|
307
|
+
it "can specify crl_number_file" do
|
308
|
+
config = R509::Config::CAConfig.new(
|
309
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
310
|
+
:crl_number_file => "crl_number_file.txt"
|
311
|
+
)
|
312
|
+
config.crl_number_file.should == 'crl_number_file.txt'
|
313
|
+
end
|
314
|
+
|
315
|
+
it "can specify crl_list_file" do
|
316
|
+
config = R509::Config::CAConfig.new(
|
317
|
+
:ca_cert => TestFixtures.test_ca_cert,
|
318
|
+
:crl_list_file => "crl_list_file.txt"
|
319
|
+
)
|
320
|
+
config.crl_list_file.should == 'crl_list_file.txt'
|
321
|
+
end
|
238
322
|
|
239
|
-
|
240
|
-
#i can test this, it's just gonna take a whole lot of floorin' it!
|
241
|
-
conf = double("conf")
|
242
|
-
engine = double("engine")
|
243
|
-
faux_key = double("faux_key")
|
244
|
-
|
245
|
-
public_key = OpenSSL::PKey::RSA.new("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqLfP8948QEZMMkZNHLDP\nOHZVrPdVvecD6bp8dz96LalMQiWjgMkJf7mPHXoNMQ5rIpntiUOmhupu+sty30+C\ndbZZIbZohioTSYq9ZIC/LC9ME12F78GRMKhBGA+ZiouzWvXqOEdMnanfSgKrlSIS\nssF71dfmOEQ08fn9Vl5jAgWmGe+v615iHqBNGr64kYooTrZYLaPlTScO1UZ76vnB\nHfNQU+tsEZNXxtZXKQqkxHxLShCOj6qmYRNn/upTZoWWd04+zXjYGEC3eKvi9ctN\n9FY+KJ6QCCa8H0Kt3cU5qyw6pzdljhbG6NKhod7OMqlGjmHdsCAYAqe3xH+V/8oe\ndwIDAQAB\n-----END PUBLIC KEY-----\n")
|
246
|
-
|
247
|
-
conf.should_receive(:kind_of?).with(Hash).and_return(true)
|
248
|
-
conf.should_receive(:[]).with("ca_cert").and_return(
|
249
|
-
"cert" => "#{File.dirname(__FILE__)}/fixtures/test_ca.cer",
|
250
|
-
"engine" => engine,
|
251
|
-
"key_name" => "r509_key"
|
252
|
-
)
|
253
|
-
conf.should_receive(:[]).at_least(1).times.and_return(nil)
|
254
|
-
conf.should_receive(:has_key?).at_least(1).times.and_return(false)
|
323
|
+
end
|
255
324
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
325
|
+
describe R509::Config::SubjectItemPolicy do
|
326
|
+
it "raises an error if you supply a non-hash" do
|
327
|
+
expect { R509::Config::SubjectItemPolicy.new('string') }.to raise_error(ArgumentError, "Must supply a hash in form 'shortname'=>'required/optional'")
|
328
|
+
end
|
329
|
+
it "raises an error if a required element is missing" do
|
330
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required")
|
331
|
+
subject = R509::Subject.new [["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
332
|
+
expect { subject_item_policy.validate_subject(subject) }.to raise_error(R509::R509Error, /This profile requires you supply/)
|
333
|
+
end
|
334
|
+
it "raises an error if your hash values are anything other than required or optional" do
|
335
|
+
expect { R509::Config::SubjectItemPolicy.new("CN" => "somethirdoption") }.to raise_error(ArgumentError, "Unknown subject item policy value. Allowed values are required and optional")
|
336
|
+
end
|
337
|
+
it "validates a subject with the same fields as the policy" do
|
338
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional")
|
339
|
+
subject = R509::Subject.new [["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
340
|
+
validated_subject = subject_item_policy.validate_subject(subject)
|
341
|
+
validated_subject.to_s.should == subject.to_s
|
342
|
+
end
|
343
|
+
it "preserves subject order when applying policies" do
|
344
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required", "C" => "required")
|
345
|
+
subject = R509::Subject.new [["C","US"],["L","Chicago"],["ST","Illinois"],["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
346
|
+
validated_subject = subject_item_policy.validate_subject(subject)
|
347
|
+
validated_subject.to_s.should == "/C=US/L=Chicago/CN=langui.sh/OU=Org Unit/O=Org"
|
348
|
+
end
|
349
|
+
it "does not match if you get case of subject_item_policy element wrong" do
|
350
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("cn" => "required")
|
351
|
+
subject = R509::Subject.new [["CN","langui.sh"]]
|
352
|
+
expect { subject_item_policy.validate_subject(subject) }.to raise_error(R509::R509Error, 'This profile requires you supply cn')
|
353
|
+
end
|
354
|
+
it "removes subject items that are not in the policy" do
|
355
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required")
|
356
|
+
subject = R509::Subject.new [["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
357
|
+
validated_subject = subject_item_policy.validate_subject(subject)
|
358
|
+
validated_subject.to_s.should == "/CN=langui.sh"
|
359
|
+
end
|
360
|
+
it "does not reorder subject items as it validates" do
|
361
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required")
|
362
|
+
subject = R509::Subject.new [["L","Chicago"],["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
363
|
+
validated_subject = subject_item_policy.validate_subject(subject)
|
364
|
+
validated_subject.to_s.should == subject.to_s
|
365
|
+
end
|
366
|
+
it "loads all the required and optional elements" do
|
367
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required", "emailAddress" => "optional")
|
368
|
+
subject_item_policy.optional.should include("OU","emailAddress")
|
369
|
+
subject_item_policy.required.should include("CN","O","L")
|
370
|
+
end
|
371
|
+
end
|
260
372
|
|
261
|
-
|
373
|
+
describe R509::Config::CAProfile do
|
374
|
+
context "validate certificate policy structure" do
|
375
|
+
it "must be an array" do
|
376
|
+
expect { R509::Config::CAProfile.new(:certificate_policies => "whatever") }.to raise_error(ArgumentError,'Not a valid certificate policy structure. Must be an array of hashes')
|
262
377
|
end
|
263
|
-
|
264
|
-
|
265
|
-
expect { R509::Config::CaConfig.from_yaml("engine_and_key", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_engine_key.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(R509::R509Error, "You can't specify both key and engine")
|
378
|
+
it "require a policy identifier" do
|
379
|
+
expect { R509::Config::CAProfile.new(:certificate_policies => [{"stuff" => "thing"}]) }.to raise_error(ArgumentError,'Each policy requires a policy identifier')
|
266
380
|
end
|
267
|
-
|
268
|
-
|
269
|
-
expect { R509::Config::CaConfig.from_yaml("engine_no_key_name", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_engine_no_key_name.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(R509::R509Error, 'You must supply a key_name with an engine')
|
381
|
+
it "the cps uri must be array of strings" do
|
382
|
+
expect { R509::Config::CAProfile.new(:certificate_policies => [{"policy_identifier" => "1.2.3.4.5", "cps_uris" => "not an array"}]) }.to raise_error(ArgumentError,'CPS URIs must be an array of strings')
|
270
383
|
end
|
271
|
-
|
272
|
-
|
273
|
-
expect{ R509::Config::CaConfig.from_yaml("no_config_here", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError)
|
384
|
+
it "user notices must be an array of hashes" do
|
385
|
+
expect { R509::Config::CAProfile.new(:certificate_policies => [{"policy_identifier" => "1.2.3.4.5", "user_notices" => "not an array"}]) }.to raise_error(ArgumentError,'User notices must be an array of hashes')
|
274
386
|
end
|
275
|
-
|
276
|
-
|
277
|
-
expect{ R509::Config::CaConfig.from_yaml("config_is_string", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"}) }.to raise_error(ArgumentError)
|
387
|
+
it "org in user notice requires notice numbers" do
|
388
|
+
expect { R509::Config::CAProfile.new(:certificate_policies => [{"policy_identifier" => "1.2.3.4.5", "user_notices" => [{"explicit_text" => "explicit", "organization" => "something"}]}]) }.to raise_error(ArgumentError,'If you provide an organization you must provide notice numbers')
|
278
389
|
end
|
279
|
-
|
280
|
-
|
281
|
-
expect{ R509::Config::CaConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures/no_directory_here"}) }.to raise_error(R509::R509Error)
|
390
|
+
it "notice numbers in user notice requires org" do
|
391
|
+
expect { R509::Config::CAProfile.new(:certificate_policies => [{"policy_identifier" => "1.2.3.4.5", "user_notices" => [{"explicit_text" => "explicit", "notice_numbers" => "1,2,3"}]}]) }.to raise_error(ArgumentError,'If you provide notice numbers you must provide an organization')
|
282
392
|
end
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
config.message_digest.should == "SHA1"
|
393
|
+
end
|
394
|
+
context "validate basic constraints structure" do
|
395
|
+
it "must be a hash with key \"ca\"" do
|
396
|
+
expect { R509::Config::CAProfile.new(:basic_constraints => 'string') }.to raise_error(ArgumentError, "You must supply a hash with a key named \"ca\" with a boolean value")
|
397
|
+
expect { R509::Config::CAProfile.new(:basic_constraints => {}) }.to raise_error(ArgumentError, "You must supply a hash with a key named \"ca\" with a boolean value")
|
289
398
|
end
|
290
|
-
|
291
|
-
|
292
|
-
config = R509::Config::CaConfig.new(
|
293
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
294
|
-
:crl_number_file => "crl_number_file.txt"
|
295
|
-
)
|
296
|
-
config.crl_number_file.should == 'crl_number_file.txt'
|
399
|
+
it "must have true or false for the ca key value" do
|
400
|
+
expect { R509::Config::CAProfile.new(:basic_constraints => {"ca" => 'truestring'}) }.to raise_error(ArgumentError, "You must supply true/false for the ca key when specifying basic constraints")
|
297
401
|
end
|
298
|
-
|
299
|
-
|
300
|
-
config = R509::Config::CaConfig.new(
|
301
|
-
:ca_cert => TestFixtures.test_ca_cert,
|
302
|
-
:crl_list_file => "crl_list_file.txt"
|
303
|
-
)
|
304
|
-
config.crl_list_file.should == 'crl_list_file.txt'
|
402
|
+
it "must not pass a path_length if ca is false" do
|
403
|
+
expect { R509::Config::CAProfile.new(:basic_constraints => {"ca" => false, "path_length" => 5}) }.to raise_error(ArgumentError, "path_length is not allowed when ca is false")
|
305
404
|
end
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
it "
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
end
|
318
|
-
it "
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
end
|
327
|
-
it "does not match if you get case of subject_item_policy element wrong" do
|
328
|
-
subject_item_policy = R509::Config::SubjectItemPolicy.new("cn" => "required")
|
329
|
-
subject = R509::Subject.new [["CN","langui.sh"]]
|
330
|
-
expect { subject_item_policy.validate_subject(subject) }.to raise_error(R509::R509Error, 'This profile requires you supply cn')
|
331
|
-
end
|
332
|
-
it "removes subject items that are not in the policy" do
|
333
|
-
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required")
|
334
|
-
subject = R509::Subject.new [["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
335
|
-
validated_subject = subject_item_policy.validate_subject(subject)
|
336
|
-
validated_subject.to_s.should == "/CN=langui.sh"
|
337
|
-
end
|
338
|
-
it "does not reorder subject items as it validates" do
|
339
|
-
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required")
|
340
|
-
subject = R509::Subject.new [["L","Chicago"],["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
|
341
|
-
validated_subject = subject_item_policy.validate_subject(subject)
|
342
|
-
validated_subject.to_s.should == subject.to_s
|
343
|
-
end
|
344
|
-
it "loads all the required and optional elements" do
|
345
|
-
subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required", "emailAddress" => "optional")
|
346
|
-
subject_item_policy.optional.should include("OU","emailAddress")
|
347
|
-
subject_item_policy.required.should include("CN","O","L")
|
405
|
+
it "must pass a non-negative integer to path_length" do
|
406
|
+
expect { R509::Config::CAProfile.new(:basic_constraints => {"ca" => true, "path_length" => -1.5}) }.to raise_error(ArgumentError, "Path length must be a non-negative integer (>= 0)")
|
407
|
+
expect { R509::Config::CAProfile.new(:basic_constraints => {"ca" => true, "path_length" => 1.5}) }.to raise_error(ArgumentError, "Path length must be a non-negative integer (>= 0)")
|
408
|
+
end
|
409
|
+
it "does not require a path_length when ca is true" do
|
410
|
+
ca_profile = R509::Config::CAProfile.new(:basic_constraints => {"ca" => true})
|
411
|
+
ca_profile.basic_constraints.should == {"ca" => true }
|
412
|
+
end
|
413
|
+
it "allows ca:false" do
|
414
|
+
ca_profile = R509::Config::CAProfile.new(:basic_constraints => {"ca" => false})
|
415
|
+
ca_profile.basic_constraints.should == {"ca" => false }
|
416
|
+
end
|
417
|
+
it "allows ca:true and a valid path length" do
|
418
|
+
ca_profile = R509::Config::CAProfile.new(:basic_constraints => {"ca" => true, "path_length" => 2})
|
419
|
+
ca_profile.basic_constraints.should == {"ca" => true, "path_length" => 2 }
|
420
|
+
end
|
421
|
+
end
|
422
|
+
context "validate key usage" do
|
423
|
+
it "errors with non-array" do
|
424
|
+
expect { R509::Config::CAProfile.new( :key_usage => 'not an array' ) }.to raise_error(ArgumentError, 'key_usage must be an array of strings (see README)')
|
348
425
|
end
|
426
|
+
it "loads properly" do
|
427
|
+
ku = ['digitalSignature']
|
428
|
+
profile = R509::Config::CAProfile.new( :key_usage => ku )
|
429
|
+
profile.key_usage.should == ku
|
430
|
+
end
|
431
|
+
end
|
432
|
+
context "validate extended key usage" do
|
433
|
+
it "errors with non-array" do
|
434
|
+
expect { R509::Config::CAProfile.new( :extended_key_usage => 'not an array' ) }.to raise_error(ArgumentError, 'extended_key_usage must be an array of strings (see README)')
|
435
|
+
end
|
436
|
+
it "loads properly" do
|
437
|
+
eku = ['serverAuth']
|
438
|
+
profile = R509::Config::CAProfile.new( :extended_key_usage => eku )
|
439
|
+
profile.extended_key_usage.should == eku
|
440
|
+
end
|
441
|
+
end
|
442
|
+
context "validate subject item policy" do
|
443
|
+
it "raises an error with an invalid subject_item_policy" do
|
444
|
+
expect { R509::Config::CAProfile.new( :subject_item_policy => "lenient!" ) }.to raise_error(ArgumentError,'subject_item_policy must be of type R509::Config::SubjectItemPolicy')
|
445
|
+
end
|
446
|
+
it "stores a valid subject_item_policy" do
|
447
|
+
policy = R509::Config::SubjectItemPolicy.new("CN" => "required")
|
448
|
+
expect { R509::Config::CAProfile.new( :subject_item_policy => policy) }.to_not raise_error
|
449
|
+
end
|
450
|
+
end
|
451
|
+
context "validate inhibit any policy" do
|
452
|
+
it "raises an error when not a number" do
|
453
|
+
expect { R509::Config::CAProfile.new( :inhibit_any_policy => "string" ) }.to raise_error(ArgumentError,'Inhibit any policy must be a non-negative integer')
|
454
|
+
end
|
455
|
+
it "raises an error when not >= 0" do
|
456
|
+
expect { R509::Config::CAProfile.new( :inhibit_any_policy => -5 ) }.to raise_error(ArgumentError,'Inhibit any policy must be a non-negative integer')
|
457
|
+
end
|
458
|
+
it "loads when providing valid data" do
|
459
|
+
profile = R509::Config::CAProfile.new(:inhibit_any_policy => 3)
|
460
|
+
profile.inhibit_any_policy.should == 3
|
461
|
+
end
|
462
|
+
end
|
463
|
+
context "validate policy constraints" do
|
464
|
+
it "raises an error when not a hash" do
|
465
|
+
expect { R509::Config::CAProfile.new( :policy_constraints => "string" ) }.to raise_error(ArgumentError,'Policy constraints must be provided as a hash with at least one of the two allowed keys: "inhibit_policy_mapping" and "require_explicit_policy"')
|
466
|
+
end
|
467
|
+
it "raises an error when no keys" do
|
468
|
+
expect { R509::Config::CAProfile.new( :policy_constraints => {} ) }.to raise_error(ArgumentError,'Policy constraints must have at least one of two keys: "inhibit_policy_mapping" and "require_explicit_policy" and the value must be non-negative')
|
469
|
+
end
|
470
|
+
it "raises an error when inhibit_policy_mapping is not valid" do
|
471
|
+
expect { R509::Config::CAProfile.new( :policy_constraints => {"inhibit_policy_mapping" => -5} ) }.to raise_error(ArgumentError,'inhibit_policy_mapping must be a non-negative integer')
|
472
|
+
end
|
473
|
+
it "raises an error when require_explicit_policy is not valid" do
|
474
|
+
expect { R509::Config::CAProfile.new( :policy_constraints => {"require_explicit_policy" => -1} ) }.to raise_error(ArgumentError,'require_explicit_policy must be a non-negative integer')
|
475
|
+
end
|
476
|
+
it "loads when provided inhibit_policy_mapping" do
|
477
|
+
profile = R509::Config::CAProfile.new( :policy_constraints => {"require_explicit_policy" => 1} )
|
478
|
+
profile.policy_constraints["require_explicit_policy"].should == 1
|
479
|
+
end
|
480
|
+
it "loads when provided require_explicit_policy" do
|
481
|
+
profile = R509::Config::CAProfile.new( :policy_constraints => {"inhibit_policy_mapping" => 0} )
|
482
|
+
profile.policy_constraints["inhibit_policy_mapping"].should == 0
|
483
|
+
end
|
484
|
+
it "loads when provided values for both keys" do
|
485
|
+
profile = R509::Config::CAProfile.new( :policy_constraints => {"require_explicit_policy" => 1, "inhibit_policy_mapping" => 4} )
|
486
|
+
profile.policy_constraints["require_explicit_policy"].should == 1
|
487
|
+
profile.policy_constraints["inhibit_policy_mapping"].should == 4
|
488
|
+
end
|
489
|
+
end
|
490
|
+
context "validate name constraints"do
|
491
|
+
it "raises an error when not a hash" do
|
492
|
+
expect { R509::Config::CAProfile.new( :name_constraints => 'a string' ) }.to raise_error(ArgumentError,'name_constraints must be provided as a hash')
|
493
|
+
end
|
494
|
+
it "raises an error when permitted and excluded are empty" do
|
495
|
+
expect { R509::Config::CAProfile.new( :name_constraints => {"permitted" => [], "excluded" => []} ) }.to raise_error(ArgumentError,'If name_constraints are supplied you must have at least one valid permitted or excluded element')
|
496
|
+
end
|
497
|
+
it "raises an error when permitted or excluded are not arrays" do
|
498
|
+
expect { R509::Config::CAProfile.new( :name_constraints => {"permitted" => 'string', "excluded" => 'string'} ) }.to raise_error(ArgumentError,'permitted must be an array')
|
499
|
+
end
|
500
|
+
it "raises an error when permitted or excluded elements are not hashes with the required values" do
|
501
|
+
expect { R509::Config::CAProfile.new( :name_constraints => {"permitted" => [{"type" => 'DNS'}]} ) }.to raise_error(ArgumentError,'Elements within the permitted array must be hashes with both type and value')
|
502
|
+
expect { R509::Config::CAProfile.new( :name_constraints => {"permitted" => [{'value' => '127'}]} ) }.to raise_error(ArgumentError,'Elements within the permitted array must be hashes with both type and value')
|
503
|
+
end
|
504
|
+
it "raises an error when an invalid type is specified" do
|
505
|
+
expect { R509::Config::CAProfile.new( :name_constraints => {"permitted" => [{'type' => 'invalid', 'value' => '127'}]} ) }.to raise_error(ArgumentError,'invalid is not an allowed type. Check R509::ASN1::GeneralName.map_type_to_tag to see a list of types')
|
506
|
+
end
|
507
|
+
it "loads a config with just permitted" do
|
508
|
+
profile = R509::Config::CAProfile.new(:name_constraints => {"permitted" => [ { 'type' => 'DNS', 'value' => 'domain.com' } ] } )
|
509
|
+
profile.name_constraints["permitted"][0]['type'] = 'DNS'
|
510
|
+
profile.name_constraints["permitted"][0]['value'] = 'domain.com'
|
511
|
+
end
|
512
|
+
it "loads a config with just excluded" do
|
513
|
+
profile = R509::Config::CAProfile.new(:name_constraints => {"excluded" => [ { 'type' => 'IP', 'value' => '127.0.0.1/255.255.255.255' } ] } )
|
514
|
+
profile.name_constraints["excluded"][0]['type'] = 'IP'
|
515
|
+
profile.name_constraints["excluded"][0]['value'] = '127.0.0.1/255.255.255.255'
|
516
|
+
end
|
517
|
+
it "loads a config with both permitted and excluded" do
|
518
|
+
profile = R509::Config::CAProfile.new(:name_constraints => {"permitted" => [ { 'type' => 'DNS', 'value' => 'domain.com' } ], "excluded" => [ { 'type' => 'IP', 'value' => '127.0.0.1/255.255.255.255' } ] } )
|
519
|
+
profile.name_constraints["permitted"][0]['type'] = 'DNS'
|
520
|
+
profile.name_constraints["permitted"][0]['value'] = 'domain.com'
|
521
|
+
profile.name_constraints["excluded"][0]['type'] = 'IP'
|
522
|
+
profile.name_constraints["excluded"][0]['value'] = '127.0.0.1/255.255.255.255'
|
523
|
+
end
|
524
|
+
end
|
525
|
+
it "initializes and stores the options provided" do
|
526
|
+
profile = R509::Config::CAProfile.new(
|
527
|
+
:basic_constraints => {"ca" => true},
|
528
|
+
:key_usage => ["digitalSignature"],
|
529
|
+
:extended_key_usage => ["serverAuth"],
|
530
|
+
:certificate_policies => [
|
531
|
+
{ "policy_identifier" => "2.16.840.1.12345.1.2.3.4.1",
|
532
|
+
"cps_uris" => ["http://example.com/cps","http://other.com/cps"],
|
533
|
+
"user_notices" => [ {"explicit_text" => "thing", "organization" => "my org", "notice_numbers" => "1,2,3,4"} ]
|
534
|
+
}
|
535
|
+
],
|
536
|
+
:ocsp_no_check => true
|
537
|
+
)
|
538
|
+
profile.basic_constraints.should == {"ca" => true}
|
539
|
+
profile.key_usage.should == ["digitalSignature"]
|
540
|
+
profile.extended_key_usage.should == ["serverAuth"]
|
541
|
+
profile.certificate_policies[0]["policy_identifier"].should == "2.16.840.1.12345.1.2.3.4.1"
|
542
|
+
profile.ocsp_no_check.should == true
|
543
|
+
end
|
544
|
+
it "initializes with expected defaults" do
|
545
|
+
profile = R509::Config::CAProfile.new
|
546
|
+
profile.basic_constraints.should == nil
|
547
|
+
profile.key_usage.should == nil
|
548
|
+
profile.extended_key_usage.should == nil
|
549
|
+
profile.certificate_policies.should == nil
|
550
|
+
profile.ocsp_no_check.should == false
|
551
|
+
profile.subject_item_policy.should == nil
|
552
|
+
end
|
553
|
+
it "loads profiles from YAML while setting expected defaults" do
|
554
|
+
config = R509::Config::CAConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
|
555
|
+
server_profile = config.profile("server") # no ocsp_no_check node
|
556
|
+
server_profile.ocsp_no_check.should == false
|
557
|
+
ocsp_profile = config.profile("ocsp_delegate_with_no_check") # ocsp_no_check => true
|
558
|
+
ocsp_profile.ocsp_no_check.should == true
|
559
|
+
client_profile = config.profile("client") # ocsp_no_check => false
|
560
|
+
client_profile.ocsp_no_check.should == false
|
561
|
+
end
|
349
562
|
end
|