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.
Files changed (203) hide show
  1. data/README.md +343 -151
  2. data/Rakefile +26 -23
  3. data/bin/r509 +126 -112
  4. data/bin/r509-parse +24 -24
  5. data/doc/R509.html +169 -7
  6. data/doc/R509/ASN1.html +370 -0
  7. data/doc/R509/ASN1/GeneralName.html +1121 -0
  8. data/doc/R509/ASN1/GeneralNames.html +843 -0
  9. data/doc/R509/ASN1/NoticeReference.html +392 -0
  10. data/doc/R509/ASN1/PolicyInformation.html +387 -0
  11. data/doc/R509/ASN1/PolicyQualifiers.html +455 -0
  12. data/doc/R509/ASN1/UserNotice.html +386 -0
  13. data/doc/R509/{Crl.html → CRL.html} +7 -7
  14. data/doc/R509/CRL/Administrator.html +1559 -0
  15. data/doc/R509/{Crl/Parser.html → CRL/SignedList.html} +501 -210
  16. data/doc/R509/{Csr.html → CSR.html} +444 -314
  17. data/doc/R509/Cert.html +866 -617
  18. data/doc/R509/Cert/Extensions.html +52 -41
  19. data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +70 -35
  20. data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +387 -4
  21. data/doc/R509/Cert/Extensions/BasicConstraints.html +61 -25
  22. data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +354 -0
  23. data/doc/R509/Cert/Extensions/CertificatePolicies.html +340 -0
  24. data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +440 -49
  25. data/doc/R509/Cert/Extensions/{CrlDistributionPoints.html → InhibitAnyPolicy.html} +52 -35
  26. data/doc/R509/Cert/Extensions/KeyUsage.html +247 -121
  27. data/doc/R509/Cert/Extensions/NameConstraints.html +445 -0
  28. data/doc/R509/Cert/Extensions/OCSPNoCheck.html +239 -0
  29. data/doc/R509/Cert/Extensions/PolicyConstraints.html +424 -0
  30. data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +437 -62
  31. data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +52 -10
  32. data/doc/R509/CertificateAuthority.html +4 -4
  33. data/doc/R509/CertificateAuthority/Signer.html +154 -187
  34. data/doc/R509/Config.html +6 -6
  35. data/doc/R509/Config/{CaConfig.html → CAConfig.html} +451 -348
  36. data/doc/R509/Config/{CaConfigPool.html → CAConfigPool.html} +47 -47
  37. data/doc/R509/Config/CAProfile.html +1015 -0
  38. data/doc/R509/Config/SubjectItemPolicy.html +86 -86
  39. data/doc/R509/IOHelpers.html +22 -22
  40. data/doc/R509/MessageDigest.html +14 -14
  41. data/doc/R509/NameSanitizer.html +53 -53
  42. data/doc/R509/{Ocsp.html → OCSP.html} +9 -9
  43. data/doc/R509/{Ocsp → OCSP}/Request.html +7 -7
  44. data/doc/R509/{Ocsp → OCSP}/Request/Nonce.html +56 -11
  45. data/doc/R509/{Ocsp → OCSP}/Response.html +44 -44
  46. data/doc/R509/{OidMapper.html → OIDMapper.html} +23 -39
  47. data/doc/R509/PrivateKey.html +415 -168
  48. data/doc/R509/R509Error.html +3 -3
  49. data/doc/R509/{Spki.html → SPKI.html} +354 -192
  50. data/doc/R509/Subject.html +224 -113
  51. data/doc/R509/Validity.html +27 -5
  52. data/doc/R509/Validity/Checker.html +13 -13
  53. data/doc/R509/Validity/DefaultChecker.html +13 -13
  54. data/doc/R509/Validity/DefaultWriter.html +14 -14
  55. data/doc/R509/Validity/Status.html +39 -39
  56. data/doc/R509/Validity/Writer.html +18 -18
  57. data/doc/_index.html +138 -35
  58. data/doc/class_list.html +1 -1
  59. data/doc/css/style.css +10 -0
  60. data/doc/file.README.html +368 -171
  61. data/doc/file.r509.html +92 -69
  62. data/doc/frames.html +1 -1
  63. data/doc/index.html +368 -171
  64. data/doc/method_list.html +910 -390
  65. data/doc/top-level-namespace.html +3 -3
  66. data/lib/r509.rb +32 -16
  67. data/lib/r509/asn1.rb +375 -0
  68. data/lib/r509/cert.rb +381 -364
  69. data/lib/r509/cert/extensions.rb +443 -76
  70. data/lib/r509/certificate_authority.rb +407 -0
  71. data/lib/r509/config.rb +547 -351
  72. data/lib/r509/crl.rb +336 -366
  73. data/lib/r509/csr.rb +278 -289
  74. data/lib/r509/ec-hack.rb +37 -0
  75. data/lib/r509/exceptions.rb +3 -3
  76. data/lib/r509/io_helpers.rb +44 -44
  77. data/lib/r509/message_digest.rb +53 -0
  78. data/lib/r509/ocsp.rb +80 -70
  79. data/lib/r509/oid_mapper.rb +32 -0
  80. data/lib/r509/private_key.rb +228 -0
  81. data/lib/r509/spki.rb +145 -93
  82. data/lib/r509/subject.rb +203 -110
  83. data/lib/r509/validity.rb +70 -68
  84. data/lib/r509/version.rb +2 -2
  85. data/r509.yaml +92 -69
  86. data/spec/asn1_spec.rb +402 -0
  87. data/spec/cert/extensions_spec.rb +957 -494
  88. data/spec/cert_spec.rb +382 -307
  89. data/spec/certificate_authority_spec.rb +668 -250
  90. data/spec/config_spec.rb +515 -302
  91. data/spec/crl_spec.rb +197 -198
  92. data/spec/csr_spec.rb +334 -289
  93. data/spec/fixtures.rb +247 -171
  94. data/spec/fixtures/cert1.der +0 -0
  95. data/spec/fixtures/cert1.pem +0 -0
  96. data/spec/fixtures/cert1_public_key_modulus.txt +0 -0
  97. data/spec/fixtures/cert3.p12 +0 -0
  98. data/spec/fixtures/cert3.pem +0 -0
  99. data/spec/fixtures/cert3_key.pem +0 -0
  100. data/spec/fixtures/cert3_key_des3.pem +0 -0
  101. data/spec/fixtures/cert4.pem +0 -0
  102. data/spec/fixtures/cert5.pem +0 -0
  103. data/spec/fixtures/cert6.pem +0 -0
  104. data/spec/fixtures/cert_expired.pem +0 -0
  105. data/spec/fixtures/cert_inhibit.pem +24 -0
  106. data/spec/fixtures/cert_name_constraints.pem +29 -0
  107. data/spec/fixtures/cert_not_yet_valid.pem +0 -0
  108. data/spec/fixtures/cert_ocsp_no_check.pem +18 -0
  109. data/spec/fixtures/cert_policy_constraints.pem +31 -0
  110. data/spec/fixtures/cert_san.pem +0 -0
  111. data/spec/fixtures/cert_san2.pem +0 -0
  112. data/spec/fixtures/cert_unknown_extension.pem +28 -0
  113. data/spec/fixtures/config_pool_test_minimal.yaml +11 -11
  114. data/spec/fixtures/config_test.yaml +54 -36
  115. data/spec/fixtures/config_test_dsa.yaml +35 -0
  116. data/spec/fixtures/config_test_ec.yaml +35 -0
  117. data/spec/fixtures/config_test_engine_key.yaml +5 -5
  118. data/spec/fixtures/config_test_engine_no_key_name.yaml +4 -4
  119. data/spec/fixtures/config_test_minimal.yaml +4 -4
  120. data/spec/fixtures/config_test_password.yaml +5 -5
  121. data/spec/fixtures/config_test_various.yaml +111 -74
  122. data/spec/fixtures/crl_list_file.txt +0 -0
  123. data/spec/fixtures/crl_with_reason.pem +0 -0
  124. data/spec/fixtures/csr1.der +0 -0
  125. data/spec/fixtures/csr1.pem +0 -0
  126. data/spec/fixtures/csr1_key.der +0 -0
  127. data/spec/fixtures/csr1_key.pem +0 -0
  128. data/spec/fixtures/csr1_key_encrypted_des3.pem +0 -0
  129. data/spec/fixtures/csr1_newlines.pem +0 -0
  130. data/spec/fixtures/csr1_no_begin_end.pem +0 -0
  131. data/spec/fixtures/csr1_public_key_modulus.txt +0 -0
  132. data/spec/fixtures/csr2.pem +0 -0
  133. data/spec/fixtures/csr2_key.pem +0 -0
  134. data/spec/fixtures/csr3.pem +0 -0
  135. data/spec/fixtures/csr4.pem +0 -0
  136. data/spec/fixtures/csr_dsa.pem +0 -0
  137. data/spec/fixtures/csr_invalid_signature.pem +0 -0
  138. data/spec/fixtures/dsa_key.pem +0 -0
  139. data/spec/fixtures/dsa_root.cer +28 -0
  140. data/spec/fixtures/dsa_root.key +20 -0
  141. data/spec/fixtures/ec_csr2.der +0 -0
  142. data/spec/fixtures/ec_csr2.pem +8 -0
  143. data/spec/fixtures/ec_key1.der +0 -0
  144. data/spec/fixtures/ec_key1.pem +6 -0
  145. data/spec/fixtures/ec_key1_encrypted.pem +9 -0
  146. data/spec/fixtures/ec_key2.pem +6 -0
  147. data/spec/fixtures/hmacsha1.sig +1 -0
  148. data/spec/fixtures/hmacsha512.sig +1 -0
  149. data/spec/fixtures/key4.pem +0 -0
  150. data/spec/fixtures/key4_encrypted_des3.pem +0 -0
  151. data/spec/fixtures/missing_key_identifier_ca.cer +0 -0
  152. data/spec/fixtures/missing_key_identifier_ca.key +0 -0
  153. data/spec/fixtures/ocsptest.r509.local.pem +0 -0
  154. data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
  155. data/spec/fixtures/ocsptest2.r509.local.pem +0 -0
  156. data/spec/fixtures/second_ca.cer +0 -0
  157. data/spec/fixtures/second_ca.key +0 -0
  158. data/spec/fixtures/spkac.der +0 -0
  159. data/spec/fixtures/spkac.txt +0 -0
  160. data/spec/fixtures/spkac_dsa.txt +1 -1
  161. data/spec/fixtures/spkac_dsa_no_verify.txt +1 -0
  162. data/spec/fixtures/spkac_ec.txt +1 -0
  163. data/spec/fixtures/spkac_rsa_newlines.txt +13 -0
  164. data/spec/fixtures/stca.pem +0 -0
  165. data/spec/fixtures/stca_ocsp_request.der +0 -0
  166. data/spec/fixtures/stca_ocsp_response.der +0 -0
  167. data/spec/fixtures/test1.csr +0 -0
  168. data/spec/fixtures/test_ca.cer +0 -0
  169. data/spec/fixtures/test_ca.key +0 -0
  170. data/spec/fixtures/test_ca.p12 +0 -0
  171. data/spec/fixtures/test_ca_des3.key +0 -0
  172. data/spec/fixtures/test_ca_ec.cer +14 -0
  173. data/spec/fixtures/test_ca_ec.key +6 -0
  174. data/spec/fixtures/test_ca_ec_ee.cer +22 -0
  175. data/spec/fixtures/test_ca_ec_ee.key +6 -0
  176. data/spec/fixtures/test_ca_ocsp.cer +0 -0
  177. data/spec/fixtures/test_ca_ocsp.key +0 -0
  178. data/spec/fixtures/test_ca_ocsp.p12 +0 -0
  179. data/spec/fixtures/test_ca_ocsp_chain.txt +0 -0
  180. data/spec/fixtures/test_ca_ocsp_response.der +0 -0
  181. data/spec/fixtures/test_ca_subroot.cer +0 -0
  182. data/spec/fixtures/test_ca_subroot.key +0 -0
  183. data/spec/fixtures/test_ca_subroot_ocsp.cer +0 -0
  184. data/spec/fixtures/test_ca_subroot_ocsp.key +0 -0
  185. data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
  186. data/spec/fixtures/unknown_oid.csr +0 -0
  187. data/spec/message_digest_spec.rb +104 -84
  188. data/spec/ocsp_spec.rb +105 -105
  189. data/spec/oid_mapper_spec.rb +21 -21
  190. data/spec/private_key_spec.rb +275 -0
  191. data/spec/r509_spec.rb +35 -0
  192. data/spec/spec_helper.rb +15 -6
  193. data/spec/spki_spec.rb +221 -142
  194. data/spec/subject_spec.rb +232 -164
  195. data/spec/validity_spec.rb +91 -91
  196. metadata +79 -25
  197. data/doc/R509/Config/CaProfile.html +0 -651
  198. data/doc/R509/Crl/Administrator.html +0 -2073
  199. data/lib/r509/certificateauthority.rb +0 -290
  200. data/lib/r509/messagedigest.rb +0 -49
  201. data/lib/r509/oidmapper.rb +0 -32
  202. data/lib/r509/privatekey.rb +0 -185
  203. 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::CaConfigPool do
6
- context "defined manually" do
7
- it "has no configs" do
8
- pool = R509::Config::CaConfigPool.new({})
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
- describe R509::Config::CaConfig do
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
- subject {@config}
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
- 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}
19
+ pool = R509::Config::CAConfigPool.new({
20
+ "first" => config
21
+ })
97
22
 
98
- it "should have the proper CA cert" do
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
- it "should have the proper CA key" do
103
- @config.ca_cert.key.to_pem.should == TestFixtures.test_ca_cert.key.to_pem
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 "raises an error if you don't pass :ca_cert" do
107
- expect { R509::Config::CaConfig.new(:crl_validity_hours => 2) }.to raise_error ArgumentError, 'Config object requires that you pass :ca_cert'
108
- end
109
- it "raises an error if :ca_cert is not of type R509::Cert" do
110
- 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'
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
- config.ocsp_cert.should == ocsp_cert
136
- end
137
- it "fails to specify a non-Config::CaProfile as the profile" do
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
- expect{ config.set_profile("bogus", "not a Config::CaProfile")}.to raise_error TypeError
43
+ pool.all.should == [config]
143
44
  end
144
45
 
145
- it "shouldn't let you specify a profile that's not a Config::CaProfile, on instantiation" do
146
- expect{ R509::Config::CaConfig.new(
147
- :ca_cert => TestFixtures.test_ca_cert,
148
- :profiles => { "first_profile" => "not a Config::CaProfile" }
149
- ) }.to raise_error TypeError
150
- end
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
- it "can specify a single profile" do
153
- first_profile = R509::Config::CaProfile.new
56
+ pool = R509::Config::CAConfigPool.new({
57
+ "first" => config1,
58
+ "second" => config2
59
+ })
154
60
 
155
- config = R509::Config::CaConfig.new(
156
- :ca_cert => TestFixtures.test_ca_cert,
157
- :profiles => { "first_profile" => first_profile }
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
- it "raises an error if you specify an invalid profile" do
164
- first_profile = R509::Config::CaProfile.new
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
- config = R509::Config::CaConfig.new(
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
- expect { config.profile("non-existent-profile") }.to raise_error(R509::R509Error, "unknown profile 'non-existent-profile'")
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
- it "should load YAML" do
175
- config = R509::Config::CaConfig.from_yaml("test_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
176
- config.crl_validity_hours.should == 72
177
- config.ocsp_validity_hours.should == 96
178
- config.message_digest.should == "SHA1"
179
- config.num_profiles.should == 3
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
- it "should load YAML which only has a CA Cert and Key defined" do
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
- it "should load YAML which has CA cert and key with password" do
215
- 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
216
- end
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
- it "should load YAML which has a PKCS12 with password" do
219
- 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
220
- end
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
- it "raises error on YAML with pkcs12 and key" do
223
- 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(R509::R509Error, "You can't specify both pkcs12 and key")
224
- end
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
- it "raises error on YAML with pkcs12 and cert" do
227
- 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(R509::R509Error, "You can't specify both pkcs12 and cert")
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
- it "raises error on YAML with pkcs12 and engine" do
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
- it "loads config with cert and no key (useful in certain cases)" do
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
- it "should load YAML which has an engine" do
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
- engine.should_receive(:respond_to?).with(:load_private_key).and_return(true)
257
- engine.should_receive(:kind_of?).with(OpenSSL::Engine).and_return(true)
258
- faux_key.should_receive(:public_key).and_return(public_key)
259
- engine.should_receive(:load_private_key).with("r509_key").and_return(faux_key)
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
- config = R509::Config::CaConfig.load_from_hash(conf)
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
- it "should fail if YAML for ca_cert contains engine and key" do
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
- it "should fail if YAML for ca_cert contains engine but no key_name" do
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
- it "should fail if YAML config is null" do
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
- it "should fail if YAML config isn't a hash" do
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
- it "should fail if YAML config doesn't give a root CA directory that's a directory" do
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
- it "should load YAML from filename" do
285
- config = R509::Config::CaConfig.load_yaml("test_ca", "#{File.dirname(__FILE__)}/fixtures/config_test.yaml", {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
286
- config.crl_validity_hours.should == 72
287
- config.ocsp_validity_hours.should == 96
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
- it "can specify crl_number_file" do
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
- it "can specify crl_list_file" do
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
- end
308
-
309
- describe R509::Config::SubjectItemPolicy do
310
- it "raises an error if you supply a non-hash" do
311
- expect { R509::Config::SubjectItemPolicy.new('string') }.to raise_error(ArgumentError, "Must supply a hash in form 'shortname'=>'required/optional'")
312
- end
313
- it "raises an error if a required element is missing" do
314
- subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional", "L" => "required")
315
- subject = R509::Subject.new [["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
316
- expect { subject_item_policy.validate_subject(subject) }.to raise_error(R509::R509Error, /This profile requires you supply/)
317
- end
318
- it "raises an error if your hash values are anything other than required or optional" do
319
- expect { R509::Config::SubjectItemPolicy.new("CN" => "somethirdoption") }.to raise_error(ArgumentError, "Unknown subject item policy value. Allowed values are required and optional")
320
- end
321
- it "validates a subject with the same fields as the policy" do
322
- subject_item_policy = R509::Config::SubjectItemPolicy.new("CN" => "required", "O" => "required", "OU" => "optional")
323
- subject = R509::Subject.new [["CN","langui.sh"],["OU","Org Unit"],["O","Org"]]
324
- validated_subject = subject_item_policy.validate_subject(subject)
325
- validated_subject.to_s.should == subject.to_s
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