r509 0.8

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 (162) hide show
  1. data/README.md +447 -0
  2. data/Rakefile +38 -0
  3. data/bin/r509 +96 -0
  4. data/bin/r509-parse +35 -0
  5. data/doc/R509.html +154 -0
  6. data/doc/R509/Cert.html +3954 -0
  7. data/doc/R509/Cert/Extensions.html +360 -0
  8. data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +391 -0
  9. data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +148 -0
  10. data/doc/R509/Cert/Extensions/BasicConstraints.html +482 -0
  11. data/doc/R509/Cert/Extensions/CrlDistributionPoints.html +316 -0
  12. data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +780 -0
  13. data/doc/R509/Cert/Extensions/KeyUsage.html +1230 -0
  14. data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +467 -0
  15. data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +216 -0
  16. data/doc/R509/CertificateAuthority.html +126 -0
  17. data/doc/R509/CertificateAuthority/Signer.html +855 -0
  18. data/doc/R509/Config.html +127 -0
  19. data/doc/R509/Config/CaConfig.html +2144 -0
  20. data/doc/R509/Config/CaConfigPool.html +599 -0
  21. data/doc/R509/Config/CaProfile.html +656 -0
  22. data/doc/R509/Config/SubjectItemPolicy.html +578 -0
  23. data/doc/R509/Crl.html +126 -0
  24. data/doc/R509/Crl/Administrator.html +2077 -0
  25. data/doc/R509/Crl/Parser.html +1224 -0
  26. data/doc/R509/Csr.html +2248 -0
  27. data/doc/R509/IOHelpers.html +564 -0
  28. data/doc/R509/MessageDigest.html +396 -0
  29. data/doc/R509/NameSanitizer.html +319 -0
  30. data/doc/R509/Ocsp.html +128 -0
  31. data/doc/R509/Ocsp/Request.html +126 -0
  32. data/doc/R509/Ocsp/Request/Nonce.html +160 -0
  33. data/doc/R509/Ocsp/Response.html +837 -0
  34. data/doc/R509/OidMapper.html +393 -0
  35. data/doc/R509/PrivateKey.html +1647 -0
  36. data/doc/R509/R509Error.html +134 -0
  37. data/doc/R509/Spki.html +1424 -0
  38. data/doc/R509/Subject.html +836 -0
  39. data/doc/R509/Validity.html +160 -0
  40. data/doc/R509/Validity/Checker.html +320 -0
  41. data/doc/R509/Validity/DefaultChecker.html +283 -0
  42. data/doc/R509/Validity/DefaultWriter.html +330 -0
  43. data/doc/R509/Validity/Status.html +561 -0
  44. data/doc/R509/Validity/Writer.html +394 -0
  45. data/doc/_index.html +501 -0
  46. data/doc/class_list.html +53 -0
  47. data/doc/css/common.css +1 -0
  48. data/doc/css/full_list.css +57 -0
  49. data/doc/css/style.css +328 -0
  50. data/doc/file.README.html +534 -0
  51. data/doc/file.r509.html +149 -0
  52. data/doc/file_list.html +58 -0
  53. data/doc/frames.html +28 -0
  54. data/doc/index.html +534 -0
  55. data/doc/js/app.js +208 -0
  56. data/doc/js/full_list.js +173 -0
  57. data/doc/js/jquery.js +4 -0
  58. data/doc/methods_list.html +1932 -0
  59. data/doc/top-level-namespace.html +112 -0
  60. data/lib/r509.rb +22 -0
  61. data/lib/r509/cert.rb +414 -0
  62. data/lib/r509/cert/extensions.rb +309 -0
  63. data/lib/r509/certificateauthority.rb +290 -0
  64. data/lib/r509/config.rb +407 -0
  65. data/lib/r509/crl.rb +379 -0
  66. data/lib/r509/csr.rb +324 -0
  67. data/lib/r509/exceptions.rb +5 -0
  68. data/lib/r509/io_helpers.rb +52 -0
  69. data/lib/r509/messagedigest.rb +49 -0
  70. data/lib/r509/ocsp.rb +85 -0
  71. data/lib/r509/oidmapper.rb +32 -0
  72. data/lib/r509/privatekey.rb +185 -0
  73. data/lib/r509/spki.rb +112 -0
  74. data/lib/r509/subject.rb +133 -0
  75. data/lib/r509/validity.rb +92 -0
  76. data/lib/r509/version.rb +4 -0
  77. data/r509.yaml +73 -0
  78. data/spec/cert/extensions_spec.rb +632 -0
  79. data/spec/cert_spec.rb +321 -0
  80. data/spec/certificate_authority_spec.rb +260 -0
  81. data/spec/config_spec.rb +349 -0
  82. data/spec/crl_spec.rb +215 -0
  83. data/spec/csr_spec.rb +302 -0
  84. data/spec/fixtures.rb +233 -0
  85. data/spec/fixtures/cert1.der +0 -0
  86. data/spec/fixtures/cert1.pem +24 -0
  87. data/spec/fixtures/cert1_public_key_modulus.txt +1 -0
  88. data/spec/fixtures/cert3.p12 +0 -0
  89. data/spec/fixtures/cert3.pem +28 -0
  90. data/spec/fixtures/cert3_key.pem +27 -0
  91. data/spec/fixtures/cert3_key_des3.pem +30 -0
  92. data/spec/fixtures/cert4.pem +14 -0
  93. data/spec/fixtures/cert5.pem +30 -0
  94. data/spec/fixtures/cert6.pem +26 -0
  95. data/spec/fixtures/cert_expired.pem +26 -0
  96. data/spec/fixtures/cert_not_yet_valid.pem +26 -0
  97. data/spec/fixtures/cert_san.pem +27 -0
  98. data/spec/fixtures/cert_san2.pem +22 -0
  99. data/spec/fixtures/config_pool_test_minimal.yaml +15 -0
  100. data/spec/fixtures/config_test.yaml +41 -0
  101. data/spec/fixtures/config_test_engine_key.yaml +7 -0
  102. data/spec/fixtures/config_test_engine_no_key_name.yaml +6 -0
  103. data/spec/fixtures/config_test_minimal.yaml +7 -0
  104. data/spec/fixtures/config_test_password.yaml +7 -0
  105. data/spec/fixtures/config_test_various.yaml +100 -0
  106. data/spec/fixtures/crl_list_file.txt +1 -0
  107. data/spec/fixtures/crl_with_reason.pem +17 -0
  108. data/spec/fixtures/csr1.der +0 -0
  109. data/spec/fixtures/csr1.pem +17 -0
  110. data/spec/fixtures/csr1_key.der +0 -0
  111. data/spec/fixtures/csr1_key.pem +27 -0
  112. data/spec/fixtures/csr1_key_encrypted_des3.pem +30 -0
  113. data/spec/fixtures/csr1_newlines.pem +32 -0
  114. data/spec/fixtures/csr1_no_begin_end.pem +15 -0
  115. data/spec/fixtures/csr1_public_key_modulus.txt +1 -0
  116. data/spec/fixtures/csr2.pem +15 -0
  117. data/spec/fixtures/csr2_key.pem +27 -0
  118. data/spec/fixtures/csr3.pem +16 -0
  119. data/spec/fixtures/csr4.pem +25 -0
  120. data/spec/fixtures/csr_dsa.pem +15 -0
  121. data/spec/fixtures/csr_invalid_signature.pem +13 -0
  122. data/spec/fixtures/dsa_key.pem +20 -0
  123. data/spec/fixtures/key4.pem +27 -0
  124. data/spec/fixtures/key4_encrypted_des3.pem +30 -0
  125. data/spec/fixtures/missing_key_identifier_ca.cer +21 -0
  126. data/spec/fixtures/missing_key_identifier_ca.key +27 -0
  127. data/spec/fixtures/ocsptest.r509.local.pem +27 -0
  128. data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
  129. data/spec/fixtures/ocsptest2.r509.local.pem +27 -0
  130. data/spec/fixtures/second_ca.cer +26 -0
  131. data/spec/fixtures/second_ca.key +27 -0
  132. data/spec/fixtures/spkac.der +0 -0
  133. data/spec/fixtures/spkac.txt +1 -0
  134. data/spec/fixtures/spkac_dsa.txt +1 -0
  135. data/spec/fixtures/stca.pem +22 -0
  136. data/spec/fixtures/stca_ocsp_request.der +0 -0
  137. data/spec/fixtures/stca_ocsp_response.der +0 -0
  138. data/spec/fixtures/test1.csr +17 -0
  139. data/spec/fixtures/test_ca.cer +22 -0
  140. data/spec/fixtures/test_ca.key +28 -0
  141. data/spec/fixtures/test_ca.p12 +0 -0
  142. data/spec/fixtures/test_ca_des3.key +30 -0
  143. data/spec/fixtures/test_ca_ocsp.cer +26 -0
  144. data/spec/fixtures/test_ca_ocsp.key +27 -0
  145. data/spec/fixtures/test_ca_ocsp.p12 +0 -0
  146. data/spec/fixtures/test_ca_ocsp_chain.txt +48 -0
  147. data/spec/fixtures/test_ca_ocsp_response.der +0 -0
  148. data/spec/fixtures/test_ca_subroot.cer +26 -0
  149. data/spec/fixtures/test_ca_subroot.key +27 -0
  150. data/spec/fixtures/test_ca_subroot_ocsp.cer +25 -0
  151. data/spec/fixtures/test_ca_subroot_ocsp.key +27 -0
  152. data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
  153. data/spec/fixtures/unknown_oid.csr +17 -0
  154. data/spec/message_digest_spec.rb +89 -0
  155. data/spec/ocsp_spec.rb +111 -0
  156. data/spec/oid_mapper_spec.rb +31 -0
  157. data/spec/privatekey_spec.rb +198 -0
  158. data/spec/spec_helper.rb +14 -0
  159. data/spec/spki_spec.rb +157 -0
  160. data/spec/subject_spec.rb +203 -0
  161. data/spec/validity_spec.rb +98 -0
  162. metadata +257 -0
data/spec/cert_spec.rb ADDED
@@ -0,0 +1,321 @@
1
+ require 'spec_helper'
2
+
3
+ describe R509::Cert do
4
+ before :all do
5
+ @cert = TestFixtures::CERT
6
+ @cert_public_key_modulus = TestFixtures::CERT_PUBLIC_KEY_MODULUS
7
+ @cert3 = TestFixtures::CERT3
8
+ @cert_der = TestFixtures::CERT_DER
9
+ @cert_san = TestFixtures::CERT_SAN
10
+ @cert_san2 = TestFixtures::CERT_SAN2
11
+ @key3 = TestFixtures::KEY3
12
+ @cert3_p12 = TestFixtures::CERT3_P12
13
+ @cert4 = TestFixtures::CERT4
14
+ @key3_encrypted = TestFixtures::KEY3_ENCRYPTED
15
+ @cert5 = TestFixtures::CERT5
16
+ @cert6 = TestFixtures::CERT6
17
+ @test_ca_cert = TestFixtures::TEST_CA_CERT
18
+ @cert_expired = TestFixtures::CERT_EXPIRED
19
+ @cert_not_yet_valid = TestFixtures::CERT_NOT_YET_VALID
20
+ end
21
+ it "raises error when no hash supplied" do
22
+ expect { R509::Cert.new('no hash')}.to raise_error(ArgumentError, 'Must provide a hash of options')
23
+ end
24
+ it "raises error when no :cert supplied" do
25
+ expect { R509::Cert.new(:key => "random")}.to raise_error(ArgumentError, 'Must provide :cert or :pkcs12')
26
+ end
27
+ it "raises error when a csr is supplied to :cert" do
28
+ expect { R509::Cert.new(:cert => TestFixtures::CSR)}.to raise_error(R509::R509Error, "Cert provided is actually a certificate signing request.")
29
+ end
30
+ it "raises error when :cert and :pkcs12 are both provided" do
31
+ expect { R509::Cert.new(
32
+ :key => @key3,
33
+ :pkcs12 => @cert3_p12,
34
+ :password => 'whatever'
35
+ ) }.to raise_error(ArgumentError, 'When providing pkcs12, do not pass cert or key')
36
+ end
37
+ it "raises error when :key and :pkcs12 are both provided" do
38
+ expect { R509::Cert.new(
39
+ :cert => @cert,
40
+ :pkcs12 => @cert3_p12,
41
+ :password => 'whatever'
42
+ ) }.to raise_error(ArgumentError, 'When providing pkcs12, do not pass cert or key')
43
+ end
44
+ it "has a public_key" do
45
+ cert = R509::Cert.new(:cert => @cert)
46
+ #this is more complex than it should have to be. diff versions of openssl
47
+ #return subtly diff PEM encodings so we need to look at the modulus (n)
48
+ #but beware, because n is not present for DSA certificates
49
+ cert.public_key.n.to_i.should == @cert_public_key_modulus.to_i
50
+ end
51
+ it "returns bit strength" do
52
+ cert = R509::Cert.new(:cert => @cert)
53
+ cert.bit_strength.should == 2048
54
+ end
55
+ it "has the right issuer" do
56
+ cert = R509::Cert.new(:cert => @cert)
57
+ cert.issuer.to_s.should == "/C=US/O=SecureTrust Corporation/CN=SecureTrust CA"
58
+ end
59
+ it "has the right issuer CN" do
60
+ cert = R509::Cert.new(:cert => @cert)
61
+ cert.issuer_cn.to_s.should == "SecureTrust CA"
62
+ end
63
+ it "generates certificate fingerprints" do
64
+ cert = R509::Cert.new(:cert => @cert)
65
+ cert.fingerprint.should == '863bbb58877b426eb10ccfd34d3056b8c961f627'
66
+ cert.fingerprint('sha256').should == '65d624f5a6937c3005d78b3f4ff09164649dd5aeb3fd8a93d6fd420e8b587fa2'
67
+ cert.fingerprint('sha512').should == 'a07d87f04161f52ef671c9d616530d07ebadef9c93c0470091617363c9ce8618dcb7931414e599d25cb032d68597111719e76d7de4bb7a92bf5ca7c08c36cf12'
68
+ cert.fingerprint('md5').should == 'aa78501c41b19252dfbe8ba509cc21f4'
69
+ end
70
+ it "returns true from has_private_key? when a key is present" do
71
+ cert = R509::Cert.new(:cert => @cert3, :key => @key3)
72
+ cert.has_private_key?.should == true
73
+ end
74
+ it "returns false from has_private_key? when a key is not present" do
75
+ cert = R509::Cert.new(:cert => @cert)
76
+ cert.has_private_key?.should == false
77
+ end
78
+ it "loads encrypted private key with cert" do
79
+ expect { R509::Cert.new(:cert => @cert3, :key => @key3_encrypted, :password => "r509") }.to_not raise_error
80
+ end
81
+ it "loads pkcs12" do
82
+ cert = R509::Cert.new(:pkcs12 => @cert3_p12, :password => "r509")
83
+ cert.has_private_key?.should == true
84
+ cert.subject.to_s.should == '/CN=futurama.com/O=Farnsworth Enterprises'
85
+ end
86
+ it "has the right not_before" do
87
+ cert = R509::Cert.new(:cert => @cert)
88
+ cert.not_before.to_i.should == 1282659002
89
+ end
90
+ it "has the right not_after" do
91
+ cert = R509::Cert.new(:cert => @cert)
92
+ cert.not_after.to_i.should == 1377267002
93
+ end
94
+ it "fetches a subject component" do
95
+ cert = R509::Cert.new(:cert => @cert)
96
+ cert.subject_component('CN').should == 'langui.sh'
97
+ end
98
+ it "fetches a subject component for mixed-case components" do
99
+ cert4 = R509::Cert.new(:cert => @cert4)
100
+ cert4.subject_component('emailAddress').should == 'support@polycom.com'
101
+
102
+ cert6 = R509::Cert.new(:cert => @cert6)
103
+ cert6.subject_component('serialNumber').should == 'a/3ILmX9qynk8f3WafoTTkKgWj0LAGRL'
104
+ end
105
+ it "returns nil when subject component not found" do
106
+ cert = R509::Cert.new(:cert => @cert)
107
+ cert.subject_component('OU').should be_nil
108
+ end
109
+ it "returns signature algorithm" do
110
+ cert = R509::Cert.new(:cert => @cert)
111
+ cert.signature_algorithm.should == 'sha1WithRSAEncryption'
112
+ end
113
+ it "returns the RSA key algorithm" do
114
+ cert = R509::Cert.new(:cert => @cert)
115
+ cert.key_algorithm.should == 'RSA'
116
+ end
117
+ it "returns the DSA key algorithm" do
118
+ cert = R509::Cert.new(:cert => @cert6)
119
+ cert.key_algorithm.should == 'DSA'
120
+ end
121
+ it "returns list of san_names when it is a san cert" do
122
+ cert = R509::Cert.new(:cert => @cert_san)
123
+ cert.san_names.should == ['langui.sh']
124
+ end
125
+ it "returns an empty list when it is not a san cert" do
126
+ cert = R509::Cert.new(:cert => @cert)
127
+ cert.san_names.should == []
128
+ end
129
+ it "#subject_names should return a list of san_names in addition to the CN" do
130
+ cert = R509::Cert.new(:cert => @cert_san2)
131
+ cert.subject_names.should == ["cn.langui.sh", "san1.langui.sh",
132
+ "san2.langui.sh", "san3.langui.sh"]
133
+ end
134
+ it "#subject_names should not have duplicates" do
135
+ cert = R509::Cert.new(:cert => @cert_san)
136
+ cert.subject_names.should == ["langui.sh"]
137
+ end
138
+ it "#subject_names should return the CN in the array, if there are no SANs" do
139
+ cert = R509::Cert.new(:cert => @cert)
140
+ cert.subject_names.should == ["langui.sh"]
141
+ end
142
+ it "raises exception when providing invalid cert" do
143
+ expect { R509::Cert.new(:cert => "invalid cert") }.to raise_error(OpenSSL::X509::CertificateError)
144
+ end
145
+ it "raises exception when providing invalid key" do
146
+ expect { R509::Cert.new(:cert => @cert, :key => 'invalid key') }.to raise_error(R509::R509Error, 'Failed to load private key. Invalid key or incorrect password.')
147
+ end
148
+ it "raises exception on non-matching key" do
149
+ expect { R509::Cert.new(:cert => @cert, :key => @key3) }.to raise_error(R509::R509Error, 'Key does not match cert.')
150
+ end
151
+ it "return normal object on matching key/cert pair" do
152
+ expect { R509::Cert.new(:cert => @cert3, :key => @key3) }.to_not raise_error
153
+ end
154
+ it "loads properly when an R509::PrivateKey is provided" do
155
+ key = R509::PrivateKey.new(:key => @key3)
156
+ expect { R509::Cert.new(:key => key, :cert => @cert3)}.to_not raise_error
157
+ end
158
+ it "writes to pem" do
159
+ cert = R509::Cert.new(:cert => @cert)
160
+ sio = StringIO.new
161
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
162
+ cert.write_pem(sio)
163
+ sio.string.should == @cert + "\n"
164
+ end
165
+ it "writes to der" do
166
+ cert = R509::Cert.new(:cert => @cert)
167
+ sio = StringIO.new
168
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
169
+ cert.write_der(sio)
170
+ sio.string.should == @cert_der
171
+ end
172
+ it "writes to pkcs12 when key/cert are present" do
173
+ cert = R509::Cert.new(:cert => @cert3, :key => @key3)
174
+ sio = StringIO.new
175
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
176
+ cert.write_pkcs12(sio,'r509_password')
177
+ expect { R509::Cert.new(:pkcs12 => sio.string, :password => 'r509_password') }.to_not raise_error
178
+ end
179
+ it "raises error when writing to pkcs12 if key is not present" do
180
+ cert = R509::Cert.new(:cert => @cert3)
181
+ expect { cert.write_pkcs12('/dev/null','password') }.to raise_error(R509::R509Error, "Writing a PKCS12 requires both key and cert")
182
+ end
183
+ it "parses san extension" do
184
+ cert = R509::Cert.new(:cert => @cert_san)
185
+ cert.san_names.should == ["langui.sh"]
186
+ end
187
+ context "when initialized with an OpenSSL::X509::Certificate" do
188
+ it "returns pem on to_pem" do
189
+ test_cert = OpenSSL::X509::Certificate.new(@cert)
190
+ cert = R509::Cert.new(:cert => test_cert)
191
+ cert.to_pem.should == @cert
192
+ end
193
+ it "returns der on to_der" do
194
+ test_cert = OpenSSL::X509::Certificate.new(@cert)
195
+ cert = R509::Cert.new(:cert => test_cert)
196
+ cert.to_der.should == @cert_der
197
+ end
198
+ it "returns pem on to_s" do
199
+ test_cert = OpenSSL::X509::Certificate.new(@cert)
200
+ cert = R509::Cert.new(:cert => test_cert)
201
+ cert.to_s.should == @cert
202
+ end
203
+ end
204
+ context "when initialized with a pem" do
205
+ it "returns on to_pem" do
206
+ cert = R509::Cert.new(:cert => @cert)
207
+ cert.to_pem.should == @cert
208
+ end
209
+ it "returns der on to_der" do
210
+ cert = R509::Cert.new(:cert => @cert)
211
+ cert.to_der.should == @cert_der
212
+ end
213
+ it "returns pem on to_s" do
214
+ cert = R509::Cert.new(:cert => @cert)
215
+ cert.to_s.should == @cert
216
+ end
217
+ end
218
+ it "gets key usage from the extensions array" do
219
+ cert = R509::Cert.new(:cert => @cert)
220
+ cert.extensions["keyUsage"].nil?.should == false
221
+ cert.extensions["keyUsage"]["value"].should == "Digital Signature, Key Encipherment"
222
+ end
223
+ it "gets extended key usage from the extensions array" do
224
+ cert = R509::Cert.new(:cert => @cert)
225
+ cert.extensions["extendedKeyUsage"].nil?.should == false
226
+ cert.extensions["extendedKeyUsage"]["value"].should == "TLS Web Server Authentication"
227
+ end
228
+
229
+ it "gets the right object from #basic_constraints" do
230
+ cert = R509::Cert.new(:cert => @cert)
231
+ cert.basic_constraints.class.should == R509::Cert::Extensions::BasicConstraints
232
+ end
233
+ it "gets the right object from #key_usage" do
234
+ cert = R509::Cert.new(:cert => @cert)
235
+ cert.key_usage.class.should == R509::Cert::Extensions::KeyUsage
236
+ end
237
+ it "gets the right object from #key_usage" do
238
+ cert = R509::Cert.new(:cert => @cert)
239
+ cert.extended_key_usage.class.should == R509::Cert::Extensions::ExtendedKeyUsage
240
+ end
241
+ it "gets the right object from #subject_key_identifier" do
242
+ cert = R509::Cert.new(:cert => @cert)
243
+ cert.subject_key_identifier.class.should == R509::Cert::Extensions::SubjectKeyIdentifier
244
+ end
245
+ it "gets the right object from #authority_key_identifier" do
246
+ cert = R509::Cert.new(:cert => @cert)
247
+ cert.authority_key_identifier.class.should == R509::Cert::Extensions::AuthorityKeyIdentifier
248
+ end
249
+ it "gets the right object from #subject_alternative_name" do
250
+ cert = R509::Cert.new(:cert => @cert5)
251
+ cert.subject_alternative_name.class.should == R509::Cert::Extensions::SubjectAlternativeName
252
+ end
253
+ it "gets the right object from #authority_info_access" do
254
+ cert = R509::Cert.new(:cert => @cert5)
255
+ cert.authority_info_access.class.should == R509::Cert::Extensions::AuthorityInfoAccess
256
+ end
257
+ it "gets the right object from #crl_distribution_points" do
258
+ cert = R509::Cert.new(:cert => @cert)
259
+ cert.crl_distribution_points.class.should == R509::Cert::Extensions::CrlDistributionPoints
260
+ end
261
+
262
+ it "checks rsa?" do
263
+ cert = R509::Cert.new(:cert => @cert)
264
+ cert.rsa?.should == true
265
+ cert.dsa?.should == false
266
+ end
267
+ it "gets RSA bit strength" do
268
+ cert = R509::Cert.new(:cert => @cert)
269
+ cert.bit_strength.should == 2048
270
+ end
271
+ it "checks dsa?" do
272
+ cert = R509::Cert.new(:cert => @cert6)
273
+ cert.rsa?.should == false
274
+ cert.dsa?.should == true
275
+ end
276
+ it "gets DSA bit strength" do
277
+ cert = R509::Cert.new(:cert => @cert6)
278
+ cert.bit_strength.should == 1024
279
+ end
280
+ it "gets serial of cert" do
281
+ cert = R509::Cert.new(:cert => @cert6)
282
+ cert.serial.should == 951504
283
+ end
284
+ it "checks a cert that is not yet valid" do
285
+ cert = R509::Cert.new(:cert => @cert_not_yet_valid)
286
+ cert.valid?.should == false
287
+ end
288
+ it "checks a cert that is in validity range" do
289
+ cert = R509::Cert.new(:cert => @test_ca_cert)
290
+ cert.valid?.should == true
291
+ end
292
+ it "checks a cert that is expired" do
293
+ cert = R509::Cert.new(:cert => @cert_expired)
294
+ cert.valid?.should == false
295
+ end
296
+ it "checks expired_at?" do
297
+ cert = R509::Cert.new(:cert => @cert_expired)
298
+ cert.valid_at?(Time.utc(2009,1,1)).should == false
299
+ cert.valid_at?(Time.utc(2011,3,1)).should == true
300
+ cert.valid_at?(1298959200).should == true
301
+ cert.valid_at?(Time.utc(2012,1,1)).should == false
302
+ end
303
+ it "is revoked by crl" do
304
+ cert = R509::Cert.new(:cert => @cert3)
305
+ crl_admin = R509::Crl::Administrator.new(TestFixtures.test_ca_config)
306
+ crl_admin.revoke_cert(1425751142578902223005775172931960716533532010870)
307
+ crl = crl_admin.to_crl
308
+ cert.is_revoked_by_crl?(crl).should == true
309
+ end
310
+ it "is not revoked by crl" do
311
+ cert = R509::Cert.new(:cert => @cert3)
312
+ crl_admin = R509::Crl::Administrator.new(TestFixtures.test_ca_config)
313
+ crl = crl_admin.to_crl
314
+ cert.is_revoked_by_crl?(crl).should == false
315
+ end
316
+ it "loads a cert with load_from_file" do
317
+ path = File.dirname(__FILE__) + '/fixtures/cert1.pem'
318
+ cert = R509::Cert.load_from_file path
319
+ cert.serial.to_i.should == 211653423715
320
+ end
321
+ end
@@ -0,0 +1,260 @@
1
+ require 'spec_helper'
2
+
3
+ describe R509::CertificateAuthority::Signer do
4
+ before :each do
5
+ @cert = TestFixtures::CERT
6
+ @csr = TestFixtures::CSR
7
+ @csr_invalid_signature = TestFixtures::CSR_INVALID_SIGNATURE
8
+ @csr3 = TestFixtures::CSR3
9
+ @test_ca_config = TestFixtures.test_ca_config
10
+ @ca = R509::CertificateAuthority::Signer.new(@test_ca_config)
11
+ @ca_no_profile = R509::CertificateAuthority::Signer.new(TestFixtures.test_ca_no_profile_config)
12
+ @spki = TestFixtures::SPKI
13
+ end
14
+
15
+ it "raises an error if you don't pass csr or spki" do
16
+ expect { @ca.sign({ :profile_name => 'server' }) }.to raise_error(ArgumentError, "You must supply either :csr or :spki")
17
+ end
18
+ it "raises an error if you pass a config that has no private key for ca_cert" do
19
+ config = R509::Config::CaConfig.new( :ca_cert => R509::Cert.new( :cert => TestFixtures::TEST_CA_CERT) )
20
+ profile = R509::Config::CaProfile.new
21
+ config.set_profile("some_profile",profile)
22
+ expect { R509::CertificateAuthority::Signer.new(config) }.to raise_error(R509::R509Error, "You must have a private key associated with your CA certificate to issue")
23
+ end
24
+ it "raises an error if you pass both csr and spki" do
25
+ csr = R509::Csr.new(:csr => @csr)
26
+ spki = R509::Spki.new(:spki => @spki, :subject=>[['CN','test']])
27
+ expect { @ca.sign({ :spki => spki, :csr => csr, :profile_name => 'server' }) }.to raise_error(ArgumentError, "You can't pass both :csr and :spki")
28
+ end
29
+ it "raise an error if you don't pass an R509::Spki in :spki" do
30
+ spki = OpenSSL::Netscape::SPKI.new(@spki)
31
+ expect { @ca.sign({ :spki => spki, :profile_name => 'server' }) }.to raise_error(ArgumentError, 'You must pass an R509::Spki object for :spki')
32
+ end
33
+ it "raise an error if you don't pass an R509::Csr in :csr" do
34
+ csr = OpenSSL::X509::Request.new(@csr)
35
+ expect { @ca.sign({ :csr => csr, :profile_name => 'server' }) }.to raise_error(ArgumentError, 'You must pass an R509::Csr object for :csr')
36
+ end
37
+ it "raises an error if you have no CaProfiles with your CaConfig when attempting to issue a cert" do
38
+ config = R509::Config::CaConfig.new(
39
+ :ca_cert => TestFixtures.test_ca_cert
40
+ )
41
+ ca = R509::CertificateAuthority::Signer.new(config)
42
+ expect { ca.sign(:csr => @csr) }.to raise_error(R509::R509Error, 'You must have at least one CaProfile on your CaConfig to issue')
43
+ end
44
+ it "properly issues server cert using spki" do
45
+ spki = R509::Spki.new(:spki => @spki, :subject=>[['CN','test.local']])
46
+ cert = @ca.sign({ :spki => spki, :profile_name => 'server' })
47
+ cert.to_pem.should match(/BEGIN CERTIFICATE/)
48
+ cert.subject.to_s.should == '/CN=test.local'
49
+ extended_key_usage = cert.extensions['extendedKeyUsage']
50
+ extended_key_usage['value'].should == 'TLS Web Server Authentication'
51
+ end
52
+ it "properly issues server cert" do
53
+ csr = R509::Csr.new(:cert => @cert, :bit_strength => 1024)
54
+ cert = @ca.sign({ :csr => csr, :profile_name => 'server' })
55
+ cert.to_pem.should match(/BEGIN CERTIFICATE/)
56
+ cert.subject.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Paul Kehrer/CN=langui.sh'
57
+ extended_key_usage = cert.extensions['extendedKeyUsage']
58
+ extended_key_usage['value'].should == 'TLS Web Server Authentication'
59
+ end
60
+ it "when supplied, uses subject_item_policy to determine allowed subject" do
61
+ csr = R509::Csr.new(:cert => @cert, :bit_strength => 512)
62
+ cert = @ca.sign({ :csr => csr, :profile_name => 'server_with_subject_item_policy' })
63
+ #profile requires C, ST, CN. O and OU are optional
64
+ cert.subject.to_s.should == '/C=US/ST=Illinois/O=Paul Kehrer/CN=langui.sh'
65
+ end
66
+ it "raises error when issuing cert with csr that does not match subject_item_policy" do
67
+ csr = R509::Csr.new(:csr => @csr)
68
+ expect { @ca.sign({ :csr => csr, :profile_name => 'server_with_subject_item_policy' }) }.to raise_error(R509::R509Error, /This profile requires you supply/)
69
+ end
70
+ it "issues with specified san domains" do
71
+ csr = R509::Csr.new(:cert => @cert, :bit_strength => 1024)
72
+ data_hash = csr.to_hash
73
+ data_hash[:san_names] = ['langui.sh','domain2.com']
74
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :data_hash => data_hash )
75
+ cert.san_names.should == ['langui.sh','domain2.com']
76
+ end
77
+ it "issues with san domains from csr" do
78
+ csr = R509::Csr.new(:csr => @csr)
79
+ cert = @ca.sign(:csr => csr, :profile_name => 'server')
80
+ cert.san_names.should == ['test.local','additionaldomains.com','saniam.com']
81
+ end
82
+ it "issues a csr made via array" do
83
+ csr = R509::Csr.new(:subject => [['CN','langui.sh']], :bit_strength => 1024)
84
+ cert = @ca.sign(:csr => csr, :profile_name => 'server')
85
+ cert.subject.to_s.should == '/CN=langui.sh'
86
+ end
87
+ it "issues a cert with the subject array provided" do
88
+ csr = R509::Csr.new(:csr => @csr)
89
+ data_hash = csr.to_hash
90
+ data_hash[:subject]['CN'] = "someotherdomain.com"
91
+ data_hash[:subject].delete("O")
92
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :data_hash => data_hash )
93
+ cert.subject.to_s.should == '/CN=someotherdomain.com'
94
+ end
95
+ it "tests that policy identifiers are properly encoded" do
96
+ csr = R509::Csr.new(:csr => @csr)
97
+ cert = @ca.sign(:csr => csr, :profile_name => 'server')
98
+ cert.extensions['certificatePolicies']['value'].should == "Policy: 2.16.840.1.12345.1.2.3.4.1\n CPS: http://example.com/cps\n"
99
+ end
100
+ it "multiple policy identifiers are properly encoded" do
101
+ csr = R509::Csr.new(:csr => @csr)
102
+ config = R509::Config::CaConfig.from_yaml("multi_policy_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
103
+ ca = R509::CertificateAuthority::Signer.new(config)
104
+ cert = ca.sign(:csr => csr, :profile_name => 'server')
105
+ cert.extensions['certificatePolicies']['value'].should == "Policy: 2.16.840.1.9999999999.3.0\nPolicy: 2.16.840.1.9999999999.1.2.3.4.1\n CPS: http://example.com/cps\n"
106
+ end
107
+ it "tests basic constraints CA:TRUE and pathlen:0 on a subroot" do
108
+ csr = R509::Csr.new(:csr => @csr)
109
+ cert = @ca.sign(:csr => csr, :profile_name => 'subroot')
110
+ cert.extensions['basicConstraints']['value'].should == 'CA:TRUE, pathlen:0'
111
+ end
112
+ it "issues with md5" do
113
+ csr = R509::Csr.new(:csr => @csr3)
114
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :message_digest => 'md5')
115
+ cert.cert.signature_algorithm.should == 'md5WithRSAEncryption'
116
+ end
117
+ it "issues with sha1" do
118
+ csr = R509::Csr.new(:csr => @csr3)
119
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :message_digest => 'sha1')
120
+ cert.cert.signature_algorithm.should == 'sha1WithRSAEncryption'
121
+ end
122
+ it "issues with sha256" do
123
+ csr = R509::Csr.new(:csr => @csr3)
124
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :message_digest => 'sha256')
125
+ cert.cert.signature_algorithm.should == 'sha256WithRSAEncryption'
126
+ end
127
+ it "issues with sha512" do
128
+ csr = R509::Csr.new(:csr => @csr3)
129
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :message_digest => 'sha512')
130
+ cert.cert.signature_algorithm.should == 'sha512WithRSAEncryption'
131
+ end
132
+ it "issues with invalid hash (sha1 fallback)" do
133
+ csr = R509::Csr.new(:csr => @csr3)
134
+ cert = @ca.sign(:csr => csr, :profile_name => 'server', :message_digest => 'invalid')
135
+ cert.cert.signature_algorithm.should == 'sha1WithRSAEncryption'
136
+ end
137
+ it "generates random serial when serial is not specified and uses microtime as part of the serial to prevent collision" do
138
+ now = Time.now
139
+ Time.stub!(:now).and_return(now)
140
+ time = now.to_i.to_s
141
+ csr = R509::Csr.new(:csr => @csr3)
142
+ cert = @ca.sign(:csr => csr, :profile_name => "server")
143
+ cert.serial.to_s.size.should >= 45
144
+ cert.serial.to_s.index(time).should_not be_nil
145
+ end
146
+ it "accepts specified serial number" do
147
+ csr = R509::Csr.new(:csr => @csr3)
148
+ cert = @ca.sign(:csr => csr, :profile_name => "server", :serial => 12345)
149
+ cert.serial.should == 12345
150
+ end
151
+ it "has default notBefore/notAfter dates" do
152
+ not_before = (Time.now - (6 * 60 * 60)).utc
153
+ not_after = (Time.now - (6 * 60 * 60) + (365 * 24 * 60 * 60)).utc
154
+ csr = R509::Csr.new(:csr => @csr3)
155
+ cert = @ca.sign(:csr => csr, :profile_name => "server")
156
+ cert.cert.not_before.year.should == not_before.year
157
+ cert.cert.not_before.month.should == not_before.month
158
+ cert.cert.not_before.day.should == not_before.day
159
+ cert.cert.not_before.hour.should == not_before.hour
160
+ cert.cert.not_before.min.should == not_before.min
161
+ cert.cert.not_after.year.should == not_after.year
162
+ cert.cert.not_after.month.should == not_after.month
163
+ cert.cert.not_after.day.should == not_after.day
164
+ cert.cert.not_after.hour.should == not_after.hour
165
+ cert.cert.not_after.min.should == not_after.min
166
+ end
167
+ it "allows you to specify notBefore/notAfter dates" do
168
+ not_before = Time.now - 5 * 60 * 60
169
+ not_after = Time.now + 5 * 60 * 60
170
+ csr = R509::Csr.new(:csr => @csr3)
171
+ cert = @ca.sign(:csr => csr, :profile_name => "server", :not_before => not_before, :not_after => not_after)
172
+ cert.cert.not_before.ctime.should == not_before.utc.ctime
173
+ cert.cert.not_after.ctime.should == not_after.utc.ctime
174
+ end
175
+ it "issues a certificate from a root that does not have a subjectKeyIdentifier" do
176
+ config = R509::Config::CaConfig.from_yaml("missing_key_identifier_ca", File.read("#{File.dirname(__FILE__)}/fixtures/config_test_various.yaml"), {:ca_root_path => "#{File.dirname(__FILE__)}/fixtures"})
177
+ ca = R509::CertificateAuthority::Signer.new(config)
178
+ csr = R509::Csr.new(:csr => @csr3)
179
+ cert = ca.sign(:csr => csr, :profile_name => "server")
180
+ cert.extensions['authorityKeyIdentifier'].should == nil
181
+ cert.extended_key_usage.web_server_authentication?.should == true
182
+ end
183
+ it "raises error unless you provide a proper config (or nil)" do
184
+ expect { R509::CertificateAuthority::Signer.new('invalid') }.to raise_error(R509::R509Error, 'config must be a kind of R509::Config::CaConfig or nil (for self-sign only)')
185
+ end
186
+ it "raises error when providing invalid ca profile" do
187
+ csr = R509::Csr.new(:csr => @csr)
188
+ expect { @ca.sign(:csr => csr, :profile_name => 'invalid') }.to raise_error(R509::R509Error, "unknown profile 'invalid'")
189
+ end
190
+ it "raises error when attempting to issue CSR with invalid signature" do
191
+ csr = R509::Csr.new(:csr => @csr_invalid_signature)
192
+ expect { @ca.sign(:csr => csr, :profile_name => 'server') }.to raise_error(R509::R509Error, 'Certificate request signature is invalid.')
193
+ end
194
+ it "raises error when passing non-hash to selfsign method" do
195
+ expect { @ca.selfsign(@csr) }.to raise_error(ArgumentError, "You must pass a hash of options consisting of at minimum :csr")
196
+ end
197
+ it "issues a self-signed certificate with custom fields" do
198
+ not_before = Time.now.to_i
199
+ not_after = Time.now.to_i+3600*24*7300
200
+ csr = R509::Csr.new(
201
+ :subject => [['C','US'],['O','r509 LLC'],['CN','r509 Self-Signed CA Test']],
202
+ :bit_strength => 1024
203
+ )
204
+ cert = @ca.selfsign(
205
+ :csr => csr,
206
+ :serial => 3,
207
+ :not_before => not_before,
208
+ :not_after => not_after,
209
+ :message_digest => 'sha256',
210
+ :san_names => ['sanname1','sanname2']
211
+ )
212
+ cert.public_key.to_s.should == csr.public_key.to_s
213
+ cert.signature_algorithm.should == 'sha256WithRSAEncryption'
214
+ cert.serial.should == 3
215
+ cert.not_before.to_i.should == not_before
216
+ cert.not_after.to_i.should == not_after
217
+ cert.subject.to_s.should == '/C=US/O=r509 LLC/CN=r509 Self-Signed CA Test'
218
+ cert.issuer.to_s.should == '/C=US/O=r509 LLC/CN=r509 Self-Signed CA Test'
219
+ cert.extensions['basicConstraints']['value'].should == 'CA:TRUE'
220
+ cert.san_names.should include('sanname1','sanname2')
221
+ end
222
+ it "issues self-signed certificate with SAN in CSR" do
223
+ csr = R509::Csr.new(
224
+ :subject => [['CN','My Self Sign']],
225
+ :san_names => ['sanname1','sanname2'],
226
+ :bit_strength => 1024
227
+ )
228
+ cert = @ca.selfsign(
229
+ :csr => csr
230
+ )
231
+ cert.san_names.should include('sanname1','sanname2')
232
+ cert.subject.to_s.should == '/CN=My Self Sign'
233
+ cert.issuer.to_s.should == '/CN=My Self Sign'
234
+ cert.public_key.to_s.should == csr.public_key.to_s
235
+ end
236
+ it "issues a self-signed certificate with defaults" do
237
+ csr = R509::Csr.new(
238
+ :subject => [['C','US'],['O','r509 LLC'],['CN','r509 Self-Signed CA Test']],
239
+ :bit_strength => 1024
240
+ )
241
+ cert = @ca.selfsign(
242
+ :csr => csr
243
+ )
244
+ cert.public_key.to_s.should == csr.public_key.to_s
245
+ cert.signature_algorithm.should == 'sha1WithRSAEncryption'
246
+ (cert.not_after.to_i-cert.not_before.to_i).should == 31536000
247
+ cert.subject.to_s.should == '/C=US/O=r509 LLC/CN=r509 Self-Signed CA Test'
248
+ cert.issuer.to_s.should == '/C=US/O=r509 LLC/CN=r509 Self-Signed CA Test'
249
+ cert.extensions['basicConstraints']['value'].should == 'CA:TRUE'
250
+ end
251
+ it "raises an error if attempting to self-sign without a key" do
252
+ csr = R509::Csr.new(:csr => @csr3)
253
+ expect { @ca.selfsign( :csr => csr ) }.to raise_error(ArgumentError, "CSR must also have a private key to self sign")
254
+ end
255
+ it "raises an error if you call sign without passing a config to the object" do
256
+ ca_signer = R509::CertificateAuthority::Signer.new
257
+ csr = R509::Csr.new(:csr => @csr3)
258
+ expect { ca_signer.sign(:csr => csr, :profile_name => "server") }.to raise_error(R509::R509Error, "When instantiating the signer without a config you can only call #selfsign")
259
+ end
260
+ end