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/crl_spec.rb CHANGED
@@ -1,215 +1,214 @@
1
1
  require 'spec_helper'
2
2
  require 'stringio'
3
3
 
4
- describe R509::Crl::Parser do
5
- before :each do
6
- @crl_reason = TestFixtures::CRL_REASON
7
- @crl = R509::Crl::Parser.new(@crl_reason)
8
- @test_ca_cert = TestFixtures::TEST_CA_CERT
9
- end
4
+ describe R509::CRL::SignedList do
5
+ before :each do
6
+ @crl_reason = TestFixtures::CRL_REASON
7
+ @crl = R509::CRL::SignedList.new(@crl_reason)
8
+ @test_ca_cert = TestFixtures::TEST_CA_CERT
9
+ end
10
10
 
11
- it "loads a crl with load_from_file" do
12
- path = File.dirname(__FILE__) + '/fixtures/crl_with_reason.pem'
13
- crl = R509::Crl::Parser.load_from_file path
14
- crl.revoked[12345].should_not be_nil
15
- end
11
+ it "loads a crl with load_from_file" do
12
+ path = File.dirname(__FILE__) + '/fixtures/crl_with_reason.pem'
13
+ crl = R509::CRL::SignedList.load_from_file path
14
+ crl.revoked[12345].should_not be_nil
15
+ end
16
16
 
17
- it "returns issuer" do
18
- @crl.issuer.to_s.should == "/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA"
19
- end
17
+ it "returns issuer" do
18
+ @crl.issuer.to_s.should == "/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA"
19
+ end
20
20
 
21
- it "returns last_update" do
22
- @crl.last_update.should == Time.at(1327446093)
23
- end
21
+ it "returns last_update" do
22
+ @crl.last_update.should == Time.at(1327446093)
23
+ end
24
24
 
25
- it "returns next_update" do
26
- @crl.next_update.should == Time.at(1328054493)
27
- end
25
+ it "returns next_update" do
26
+ @crl.next_update.should == Time.at(1328054493)
27
+ end
28
28
 
29
- it "returns signature_algorithm" do
30
- @crl.signature_algorithm.should == "sha1WithRSAEncryption"
31
- end
29
+ it "returns signature_algorithm" do
30
+ @crl.signature_algorithm.should == "sha1WithRSAEncryption"
31
+ end
32
32
 
33
- it "verifies the CRL signature" do
34
- cert = R509::Cert.new(:cert => @test_ca_cert)
35
- @crl.verify(cert.public_key).should == true
36
- end
33
+ it "verifies the CRL signature" do
34
+ cert = R509::Cert.new(:cert => @test_ca_cert)
35
+ @crl.verify(cert.public_key).should == true
36
+ end
37
37
 
38
- it "checks if a serial is revoked?" do
39
- @crl.revoked?(111111).should == false
40
- @crl.revoked?(12345).should == true
41
- end
38
+ it "checks if a serial is revoked?" do
39
+ @crl.revoked?(111111).should == false
40
+ @crl.revoked?(12345).should == true
41
+ end
42
42
 
43
- it "returns a hash of all revoked certs" do
44
- @crl.revoked[12345][:time].should == Time.at(1327449693)
45
- @crl.revoked[12345][:reason].should == "Key Compromise"
46
- @crl.revoked[123456][:time].should == Time.at(1327449693)
47
- @crl.revoked[123456][:reason].should == "Unspecified"
48
- @crl.revoked[1234567][:time].should == Time.at(1327449693)
49
- @crl.revoked[1234567][:reason].should == "Unspecified"
50
- @crl.revoked[12345678].should == nil
51
- end
43
+ it "returns a hash of all revoked certs" do
44
+ @crl.revoked[12345][:time].should == Time.at(1327449693)
45
+ @crl.revoked[12345][:reason].should == "Key Compromise"
46
+ @crl.revoked[123456][:time].should == Time.at(1327449693)
47
+ @crl.revoked[123456][:reason].should == "Unspecified"
48
+ @crl.revoked[1234567][:time].should == Time.at(1327449693)
49
+ @crl.revoked[1234567][:reason].should == "Unspecified"
50
+ @crl.revoked[12345678].should == nil
51
+ end
52
52
 
53
- it "returns revocation information for a serial" do
54
- @crl.revoked_cert(11111).should == nil
55
- revoked_info = @crl.revoked_cert(12345)
56
- revoked_info[:time].should == Time.at(1327449693)
57
- revoked_info[:reason].should == "Key Compromise"
58
- end
53
+ it "returns revocation information for a serial" do
54
+ @crl.revoked_cert(11111).should == nil
55
+ revoked_info = @crl.revoked_cert(12345)
56
+ revoked_info[:time].should == Time.at(1327449693)
57
+ revoked_info[:reason].should == "Key Compromise"
58
+ end
59
+
60
+ it "returns der" do
61
+ @crl.to_der.should_not be_nil
62
+ end
63
+ it "returns pem" do
64
+ @crl.to_pem.should_not be_nil
65
+ end
66
+ it "writes to pem" do
67
+ sio = StringIO.new
68
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
69
+ @crl.write_pem(sio)
70
+ parsed_crl = R509::CRL::SignedList.new(sio.string)
71
+ parsed_crl.issuer.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA'
72
+ parsed_crl.issuer.CN.should == 'Test CA'
73
+ end
74
+ it "writes to der" do
75
+ sio = StringIO.new
76
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
77
+ @crl.write_der(sio)
78
+ parsed_crl = R509::CRL::SignedList.new(sio.string)
79
+ parsed_crl.issuer.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA'
80
+ parsed_crl.issuer.CN.should == 'Test CA'
81
+ end
59
82
  end
60
83
 
61
- describe R509::Crl::Administrator do
62
- before :each do
63
- @cert = TestFixtures::CERT
64
- @csr = TestFixtures::CSR
65
- @csr3 = TestFixtures::CSR3
66
- @test_ca_config = TestFixtures.test_ca_no_profile_config
67
- end
68
- it "generates CRL with no entries in revocation list" do
69
- crl = R509::Crl::Administrator.new(@test_ca_config)
70
- crl.generate_crl
71
- crl.to_pem.should match(/BEGIN X509 CRL/)
72
- end
73
- it "raises exception when no R509::Config::CaConfig object is passed to the constructor" do
74
- expect { R509::Crl::Administrator.new(['random']) }.to raise_error(R509::R509Error)
75
- end
76
- it "can write the crl_number_file" do
77
- crl = R509::Crl::Administrator.new(@test_ca_config)
78
- crl.crl_number_file.string.should == "1"
79
- crl.crl_number_file.reopen("")
80
- crl.save_crl_number
81
- crl.crl_number_file.string.should == "1"
82
- end
83
- it "adds a cert to the revocation list" do
84
- crl = R509::Crl::Administrator.new(@test_ca_config)
85
- crl.revoked?(383834832).should == false
86
- crl.revoke_cert(383834832)
87
- crl.revoked?(383834832).should == true
88
- parsed_crl = OpenSSL::X509::CRL.new(crl.to_der)
89
- parsed_crl.revoked[0].serial.should == 383834832
90
- end
91
- it "can revoke (with reason)" do
92
- crl = R509::Crl::Administrator.new(@test_ca_config)
93
- crl.revoked?(12345).should == false
94
- crl.revoke_cert(12345, 1)
95
- crl.revoked?(12345).should == true
96
- crl.revoked_cert(12345)[:reason].should == 1
84
+ describe R509::CRL::Administrator do
85
+ before :each do
86
+ @cert = TestFixtures::CERT
87
+ @csr = TestFixtures::CSR
88
+ @csr3 = TestFixtures::CSR3
89
+ @test_ca_config = TestFixtures.test_ca_no_profile_config
90
+ @test_ca_dsa_config = TestFixtures.test_ca_dsa_no_profile_config
91
+ end
92
+ it "generates CRL with no entries in revocation list (RSA key)" do
93
+ crl_admin = R509::CRL::Administrator.new(@test_ca_config)
94
+ crl_admin.generate_crl
95
+ crl_admin.crl.to_pem.should match(/BEGIN X509 CRL/)
96
+ crl_admin.crl.signature_algorithm.should == 'sha1WithRSAEncryption'
97
+ end
98
+ it "generates CRL with no entries in revocation list (DSA key)" do
99
+ crl_admin = R509::CRL::Administrator.new(@test_ca_dsa_config)
100
+ crl_admin.generate_crl
101
+ crl_admin.crl.to_pem.should match(/BEGIN X509 CRL/)
102
+ crl_admin.crl.signature_algorithm.should == 'dsaWithSHA1'
103
+ end
104
+ context "elliptic curve", :ec => true do
105
+ before :all do
106
+ @test_ca_ec_config = TestFixtures.test_ca_ec_no_profile_config
107
+ end
108
+ it "generates CRL with no entries in revocation list (EC key)" do
109
+ crl_admin = R509::CRL::Administrator.new(@test_ca_ec_config)
110
+ crl_admin.generate_crl
111
+ crl_admin.crl.to_pem.should match(/BEGIN X509 CRL/)
112
+ crl_admin.crl.signature_algorithm.should == 'ecdsa-with-SHA1'
113
+ end
114
+ end
115
+ it "raises exception when no R509::Config::CAConfig object is passed to the constructor" do
116
+ expect { R509::CRL::Administrator.new(['random']) }.to raise_error(R509::R509Error)
117
+ end
118
+ it "can write the crl_number_file" do
119
+ crl = R509::CRL::Administrator.new(@test_ca_config)
120
+ crl.crl_number_file.string.should == "1"
121
+ crl.crl_number_file.reopen("")
122
+ crl.save_crl_number
123
+ crl.crl_number_file.string.should == "1"
124
+ end
125
+ it "adds a cert to the revocation list" do
126
+ crl_admin = R509::CRL::Administrator.new(@test_ca_config)
127
+ crl_admin.revoked?(383834832).should == false
128
+ crl_admin.revoke_cert(383834832)
129
+ crl_admin.revoked?(383834832).should == true
130
+ crl_admin.crl.revoked[383834832].should_not be_nil
131
+ crl_admin.crl.crl.revoked[0].serial.should == 383834832
132
+ end
133
+ it "can revoke (with reason)" do
134
+ crl_admin = R509::CRL::Administrator.new(@test_ca_config)
135
+ crl_admin.revoked?(12345).should == false
136
+ crl_admin.revoke_cert(12345, 1)
137
+ crl_admin.revoked?(12345).should == true
138
+ crl_admin.revoked_cert(12345)[:reason].should == 1
97
139
 
98
- parsed_crl = OpenSSL::X509::CRL.new(crl.to_pem)
99
- parsed_crl.revoked[0].serial.should == 12345
100
- parsed_crl.revoked[0].extensions[0].oid.should == "CRLReason"
101
- parsed_crl.revoked[0].extensions[0].value.should == "Key Compromise"
102
- end
103
- it "cannot revoke the same serial twice" do
104
- crl = R509::Crl::Administrator.new(@test_ca_config)
105
- crl.revoked?(12345).should == false
106
- crl.revoke_cert(12345, 1)
107
- crl.revoked?(12345).should == true
108
- crl.revoked_cert(12345)[:reason].should == 1
109
- expect { crl.revoke_cert(12345, 1) }.to raise_error(R509::R509Error, "Cannot revoke a previously revoked certificate")
110
- crl.revoked?(12345).should == true
111
- end
112
- it "adds a cert to the revocation list with an invalid reason code" do
113
- crl = R509::Crl::Administrator.new(@test_ca_config)
114
- crl.revoke_cert(383834832,15)
115
- crl.generate_crl.should match(/BEGIN X509 CRL/)
116
- crl.revoked?(383834832).should == true
117
- crl.revoked_cert(383834832)[:reason].should == 0
118
- end
119
- it "removes a cert from the revocation list" do
120
- crl = R509::Crl::Administrator.new(@test_ca_config)
121
- crl.revoke_cert(383834832)
122
- crl.revoked?(383834832).should == true
123
- parsed_crl = OpenSSL::X509::CRL.new(crl.to_pem)
124
- parsed_crl.revoked[0].serial.should == 383834832
125
- crl.unrevoke_cert(383834832)
126
- crl.revoked?(383834832).should == false
127
- parsed_crl = OpenSSL::X509::CRL.new(crl.to_pem)
128
- parsed_crl.revoked.empty?.should == true
129
- end
130
- it "loads an existing revocation list file" do
131
- config = R509::Config::CaConfig.new(
132
- :ca_cert => TestFixtures.test_ca_cert,
133
- :crl_list_file => TestFixtures::CRL_LIST_FILE
134
- )
135
- crl = R509::Crl::Administrator.new(config)
136
- crl.revoked?(12345).should == true
137
- crl.revoked_cert(12345)[:revoke_time].should == 1323983885
138
- crl.revoked_cert(12345)[:reason].should == 0
140
+ crl_admin.crl.crl.revoked[0].serial.should == 12345
141
+ crl_admin.crl.crl.revoked[0].extensions[0].oid.should == "CRLReason"
142
+ crl_admin.crl.crl.revoked[0].extensions[0].value.should == "Key Compromise"
143
+ end
144
+ it "cannot revoke the same serial twice" do
145
+ crl = R509::CRL::Administrator.new(@test_ca_config)
146
+ crl.revoked?(12345).should == false
147
+ crl.revoke_cert(12345, 1)
148
+ crl.revoked?(12345).should == true
149
+ crl.revoked_cert(12345)[:reason].should == 1
150
+ expect { crl.revoke_cert(12345, 1) }.to raise_error(R509::R509Error, "Cannot revoke a previously revoked certificate")
151
+ crl.revoked?(12345).should == true
152
+ end
153
+ it "adds a cert to the revocation list with an invalid reason code" do
154
+ crl = R509::CRL::Administrator.new(@test_ca_config)
155
+ crl.revoke_cert(383834832,15)
156
+ crl.generate_crl.should match(/BEGIN X509 CRL/)
157
+ crl.revoked?(383834832).should == true
158
+ crl.revoked_cert(383834832)[:reason].should == 0
159
+ end
160
+ it "removes a cert from the revocation list" do
161
+ crl_admin = R509::CRL::Administrator.new(@test_ca_config)
162
+ crl_admin.revoke_cert(383834832)
163
+ crl_admin.revoked?(383834832).should == true
164
+ crl_admin.crl.crl.revoked[0].serial.should == 383834832
165
+ crl_admin.unrevoke_cert(383834832)
166
+ crl_admin.revoked?(383834832).should == false
167
+ crl_admin.crl.crl.revoked.empty?.should == true
168
+ end
169
+ it "loads an existing revocation list file" do
170
+ config = R509::Config::CAConfig.new(
171
+ :ca_cert => TestFixtures.test_ca_cert,
172
+ :crl_list_file => TestFixtures::CRL_LIST_FILE
173
+ )
174
+ crl = R509::CRL::Administrator.new(config)
175
+ crl.revoked?(12345).should == true
176
+ crl.revoked_cert(12345)[:revoke_time].should == 1323983885
177
+ crl.revoked_cert(12345)[:reason].should == 0
139
178
 
140
- end
141
- it "when nil crl_list_file still call generate_crl" do
142
- config = R509::Config::CaConfig.new(
143
- :ca_cert => TestFixtures.test_ca_cert,
144
- :crl_list_file => nil
145
- )
146
- crl = R509::Crl::Administrator.new(config)
147
- crl.to_pem.should match(/BEGIN X509 CRL/)
148
- end
149
- it "sets validity via yaml" do
150
- crl = R509::Crl::Administrator.new(@test_ca_config)
151
- now = Time.at Time.now.to_i
152
- crl.generate_crl
153
- crl.next_update.should == (now+168*3600) #default 168 hours (7 days)
154
- end
155
- it "has a last_update time" do
156
- crl = R509::Crl::Administrator.new(@test_ca_config)
157
- now = Time.at Time.now.to_i
158
- crl.generate_crl
159
- crl.last_update.should == (now - @test_ca_config.crl_start_skew_seconds)
160
- end
161
- it "returns der" do
162
- crl = R509::Crl::Administrator.new(@test_ca_config)
163
- crl.generate_crl
164
- parsed_crl = crl.to_crl
165
- parsed_crl.issuer.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA'
166
- parsed_crl.issuer_cn.should == 'Test CA'
167
- end
168
- it "returns pem" do
169
- crl = R509::Crl::Administrator.new(@test_ca_config)
170
- crl.generate_crl
171
- parsed_crl = crl.to_crl
172
- parsed_crl.issuer.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA'
173
- parsed_crl.issuer_cn.should == 'Test CA'
174
- end
175
- it "writes to pem" do
176
- crl = R509::Crl::Administrator.new(@test_ca_config)
177
- crl.generate_crl
178
- sio = StringIO.new
179
- sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
180
- crl.write_pem(sio)
181
- parsed_crl = R509::Crl::Parser.new(sio.string)
182
- parsed_crl.issuer.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA'
183
- parsed_crl.issuer_cn.should == 'Test CA'
184
- end
185
- it "writes to der" do
186
- crl = R509::Crl::Administrator.new(@test_ca_config)
187
- crl.generate_crl
188
- sio = StringIO.new
189
- sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
190
- crl.write_der(sio)
191
- parsed_crl = R509::Crl::Parser.new(sio.string)
192
- parsed_crl.issuer.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Ruby CA Project/CN=Test CA'
193
- parsed_crl.issuer_cn.should == 'Test CA'
194
- end
195
- it "writes crl list" do
196
- crl = R509::Crl::Administrator.new(@test_ca_config)
197
- crl.revoke_cert(12345)
198
- crl.save_crl_list
199
- crl.crl_list_file.string.should match(/[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+/)
200
- end
201
- it "doesn't write the crl_number_file when it is nil" do
202
- config = R509::Config::CaConfig.new(
203
- :ca_cert => TestFixtures.test_ca_cert
204
- )
205
- crl = R509::Crl::Administrator.new(config)
206
- expect { crl.save_crl_number }.to_not raise_error(StandardError)
207
- end
208
- it "doesn't write the crl_list_file when it is nil" do
209
- config = R509::Config::CaConfig.new(
210
- :ca_cert => TestFixtures.test_ca_cert
211
- )
212
- crl = R509::Crl::Administrator.new(config)
213
- expect { crl.save_crl_list }.to_not raise_error(StandardError)
214
- end
179
+ end
180
+ it "when nil crl_list_file still call generate_crl" do
181
+ config = R509::Config::CAConfig.new(
182
+ :ca_cert => TestFixtures.test_ca_cert,
183
+ :crl_list_file => nil
184
+ )
185
+ crl_admin = R509::CRL::Administrator.new(config)
186
+ crl_admin.crl.to_pem.should match(/BEGIN X509 CRL/)
187
+ end
188
+ it "sets validity via yaml" do
189
+ crl_admin = R509::CRL::Administrator.new(@test_ca_config)
190
+ now = Time.at Time.now.to_i
191
+ crl_admin.generate_crl
192
+ crl_admin.crl.next_update.should == (now+168*3600) #default 168 hours (7 days)
193
+ end
194
+ it "writes crl list" do
195
+ crl = R509::CRL::Administrator.new(@test_ca_config)
196
+ crl.revoke_cert(12345)
197
+ crl.save_crl_list
198
+ crl.crl_list_file.string.should match(/[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+/)
199
+ end
200
+ it "doesn't write the crl_number_file when it is nil" do
201
+ config = R509::Config::CAConfig.new(
202
+ :ca_cert => TestFixtures.test_ca_cert
203
+ )
204
+ crl = R509::CRL::Administrator.new(config)
205
+ expect { crl.save_crl_number }.to_not raise_error(StandardError)
206
+ end
207
+ it "doesn't write the crl_list_file when it is nil" do
208
+ config = R509::Config::CAConfig.new(
209
+ :ca_cert => TestFixtures.test_ca_cert
210
+ )
211
+ crl = R509::CRL::Administrator.new(config)
212
+ expect { crl.save_crl_list }.to_not raise_error(StandardError)
213
+ end
215
214
  end
data/spec/csr_spec.rb CHANGED
@@ -3,300 +3,345 @@ require 'stringio'
3
3
  require 'r509/csr'
4
4
 
5
5
 
6
- describe R509::Csr do
6
+ describe R509::CSR do
7
+ before :all do
8
+ @csr = TestFixtures::CSR
9
+ @csr_newlines = TestFixtures::CSR_NEWLINES
10
+ @csr_no_begin_end = TestFixtures::CSR_NO_BEGIN_END
11
+ @csr_der = TestFixtures::CSR_DER
12
+ @csr_public_key_modulus = TestFixtures::CSR_PUBLIC_KEY_MODULUS
13
+ @csr_invalid_signature = TestFixtures::CSR_INVALID_SIGNATURE
14
+ @csr2 = TestFixtures::CSR2
15
+ @csr3 = TestFixtures::CSR3
16
+ @csr_der = TestFixtures::CSR_DER
17
+ @csr_dsa = TestFixtures::CSR_DSA
18
+ @csr4_multiple_attrs = TestFixtures::CSR4_MULTIPLE_ATTRS
19
+ @key3 = TestFixtures::KEY3
20
+ @key_csr2 = TestFixtures::KEY_CSR2
21
+ @dsa_key = TestFixtures::DSA_KEY
22
+ @csr_unknown_oid = TestFixtures::CSR_UNKNOWN_OID
23
+ @ec_csr2_pem = TestFixtures::EC_CSR2_PEM
24
+ @ec_csr2_der = TestFixtures::EC_CSR2_DER
25
+ end
26
+
27
+ it "raises an exception when passing non-hash" do
28
+ expect { R509::CSR.new('invalid') }.to raise_error(ArgumentError, 'Must provide a hash of options')
29
+ end
30
+ it "key creation defaults to RSA when no type or key is passed" do
31
+ csr = R509::CSR.new(:subject => [['CN','testing.rsa']], :bit_strength => 1024)
32
+ csr.rsa?.should == true
33
+ csr.dsa?.should == false
34
+ csr.ec?.should == false
35
+ end
36
+ it "returns expected value for to_der" do
37
+ csr = R509::CSR.new(:csr => @csr)
38
+ csr.to_der.should == @csr_der
39
+ end
40
+ it "loads a csr with extraneous newlines" do
41
+ csr = R509::CSR.new(:csr => @csr_newlines)
42
+ csr.to_pem.should match(/-----BEGIN CERTIFICATE REQUEST-----/)
43
+ end
44
+ it "loads a csr with no begin/end lines" do
45
+ csr = R509::CSR.new(:csr => @csr_no_begin_end)
46
+ csr.to_pem.should match(/-----BEGIN CERTIFICATE REQUEST-----/)
47
+ end
48
+ it "returns true from #has_private_key? when private key is present" do
49
+ csr = R509::CSR.new(:bit_strength => 512, :subject => [['CN','private-key-check.com']])
50
+ csr.has_private_key?.should == true
51
+ end
52
+ it "returns false from #has_private_key? when private key is not present" do
53
+ csr = R509::CSR.new(:csr => @csr)
54
+ csr.has_private_key?.should == false
55
+ end
56
+ it "key creation defaults to 2048 when no bit strength or key is passed" do
57
+ csr = R509::CSR.new(:subject => [['CN','testing2048.rsa']])
58
+ csr.bit_strength.should == 2048
59
+ end
60
+ it "creates a CSR when a key is provided" do
61
+ csr = R509::CSR.new(:key => @key3, :subject => [['CN','pregenerated.com']], :bit_strength => 1024)
62
+ csr.to_pem.should match(/CERTIFICATE REQUEST/)
63
+ #validate the CSR matches the key
64
+ csr.req.verify(csr.key.public_key).should == true
65
+ end
66
+ it "loads successfully when an R509::PrivateKey is provided" do
67
+ key = R509::PrivateKey.new(:key => @key3)
68
+ expect { R509::CSR.new(:key => key, :csr => @csr3)}.to_not raise_error
69
+ end
70
+ it "raises an exception when you pass :subject and :csr" do
71
+ expect { R509::CSR.new(:subject => [['CN','error.com']], :csr => @csr) }.to raise_error(ArgumentError,'You must provide :subject or :csr, not both')
72
+ end
73
+ it "raises an exception for not providing valid type when key is nil" do
74
+ expect { R509::CSR.new(:subject => [['CN','error.com']], :type => :invalid_symbol) }.to raise_error(ArgumentError,'Must provide :rsa, :dsa, or :ec as type when key is nil')
75
+ end
76
+ it "raises an exception when you don't provide :subject or :csr" do
77
+ expect { R509::CSR.new(:bit_strength => 1024) }.to raise_error(ArgumentError,'You must provide :subject or :csr')
78
+ end
79
+ it "raises an exception if you provide a list of domains with an existing CSR" do
80
+ expect { R509::CSR.new(:csr => @csr, :san_names => ['moredomainsiwanttoadd.com']) }.to raise_error(ArgumentError,'You can\'t add domains to an existing CSR')
81
+ end
82
+ it "changes the message_digest to DSS1 when passed a DSA key" do
83
+ csr = R509::CSR.new(:subject => [["CN","dsasigned.com"]], :key => @dsa_key)
84
+ csr.message_digest.name.should == 'dss1'
85
+ csr.signature_algorithm.should == 'dsaWithSHA1'
86
+ #dss1 is actually the same as SHA1
87
+ #Yes this is confusing
88
+ #see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKey/DSA.html
89
+ end
90
+ it "changes the message_digest to DSS1 when creating a DSA key" do
91
+ csr = R509::CSR.new(:subject => [["CN","dsasigned.com"]], :type => :dsa, :bit_strength => 512)
92
+ csr.message_digest.name.should == 'dss1'
93
+ csr.signature_algorithm.should == 'dsaWithSHA1'
94
+ #dss1 is actually the same as SHA1
95
+ #Yes this is confusing
96
+ #see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKey/DSA.html
97
+ end
98
+ it "signs a CSR properly when passed a DSA key" do
99
+ csr = R509::CSR.new(:subject => [["CN","dsasigned.com"]], :key => @dsa_key)
100
+ csr.verify_signature.should == true
101
+ end
102
+ it "signs a CSR properly when creating a DSA key" do
103
+ csr = R509::CSR.new(:subject => [["CN","dsasigned.com"]], :type => :dsa, :bit_strength => 512)
104
+ csr.verify_signature.should == true
105
+ end
106
+ it "writes to pem" do
107
+ csr = R509::CSR.new(:csr => @csr)
108
+ sio = StringIO.new
109
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
110
+ csr.write_pem(sio)
111
+ sio.string.should == @csr
112
+ end
113
+ it "writes to der" do
114
+ sio = StringIO.new
115
+ sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
116
+ csr = R509::CSR.new(:csr => @csr)
117
+ csr.write_der(sio)
118
+ sio.string.should == @csr_der
119
+ end
120
+ it "duplicate SAN names should be removed" do
121
+ csr = R509::CSR.new( :bit_strength => 512, :subject => [['CN','test2345.com']], :san_names => ["test2.local","test.local","test.local"] )
122
+ csr.san.dns_names.should == ["test2.local", "test.local"]
123
+ end
124
+ it "san is nil when there are no SAN names" do
125
+ csr = R509::CSR.new( :subject => [['CN','langui.sh'],['emailAddress','ca@langui.sh']], :bit_strength => 512)
126
+ csr.san.nil?.should == true
127
+ end
128
+ context "when initialized" do
129
+ it "raises exception when providing invalid csr" do
130
+ expect { R509::CSR.new({:csr => 'invalid csr'}) }.to raise_error(OpenSSL::X509::RequestError)
131
+ end
132
+ it "raises exception when providing invalid key" do
133
+ expect { R509::CSR.new({:csr => @csr, :key => 'invalid key'}) }.to raise_error(R509::R509Error,"Failed to load private key. Invalid key or incorrect password.")
134
+ end
135
+ end
136
+ context "when passing a subject array" do
137
+ it "generates a matching CSR" do
138
+ csr = R509::CSR.new( :subject=> [['CN','langui.sh'],['ST','Illinois'],['L','Chicago'],['C','US'],['emailAddress','ca@langui.sh']], :bit_strength => 1024)
139
+ csr.subject.to_s.should == '/CN=langui.sh/ST=Illinois/L=Chicago/C=US/emailAddress=ca@langui.sh'
140
+ end
141
+ it "generates a matching csr when supplying raw oids" do
142
+ csr = R509::CSR.new( :subject => [['2.5.4.3','common name'],['2.5.4.15','business category'],['2.5.4.7','locality'],['1.3.6.1.4.1.311.60.2.1.3','jurisdiction oid openssl typically does not know']], :bit_strength => 1024 )
143
+ csr.subject.to_s.should == "/CN=common name/businessCategory=business category/L=locality/jurisdictionOfIncorporationCountryName=jurisdiction oid openssl typically does not know"
144
+ end
145
+ it "adds SAN names to a generated CSR" do
146
+ csr = R509::CSR.new( :subject => [['CN','test']], :bit_strength => 1024, :san_names => ['1.2.3.4','http://langui.sh','email@address.local','domain.internal','2.3.4.5',[['CN','dirnametest']]])
147
+ csr.san.ip_addresses.should == ['1.2.3.4','2.3.4.5']
148
+ csr.san.dns_names.should == ['domain.internal']
149
+ csr.san.uris.should == ['http://langui.sh']
150
+ csr.san.rfc_822_names.should == ['email@address.local']
151
+ csr.san.directory_names.size.should == 1
152
+ csr.san.directory_names[0].to_s.should == '/CN=dirnametest'
153
+ end
154
+ end
155
+ context "when supplying an existing csr" do
156
+ it "populates the bit_strength" do
157
+ csr = R509::CSR.new({ :csr => @csr })
158
+ csr.bit_strength.should == 2048
159
+ end
160
+ it "populates the subject" do
161
+ csr = R509::CSR.new({ :csr => @csr })
162
+ csr.subject.to_s.should == '/CN=test.local/O=Testing CSR'
163
+ end
164
+ it "parses the san names" do
165
+ csr = R509::CSR.new({ :csr => @csr })
166
+ csr.san.dns_names.should == ["test.local", "additionaldomains.com", "saniam.com"]
167
+ end
168
+ it "parses san names when there are multiple non-SAN attributes" do
169
+ csr = R509::CSR.new({ :csr => @csr4_multiple_attrs })
170
+ csr.san.dns_names.should == ["adomain.com", "anotherdomain.com", "justanexample.com"]
171
+ end
172
+ it "fetches a subject component" do
173
+ csr = R509::CSR.new({ :csr => @csr })
174
+ csr.subject_component('CN').to_s.should == 'test.local'
175
+ end
176
+ it "returns nil when subject component not found" do
177
+ csr = R509::CSR.new({ :csr => @csr })
178
+ csr.subject_component('OU').should be_nil
179
+ end
180
+ it "returns the signature algorithm" do
181
+ csr = R509::CSR.new({ :csr => @csr })
182
+ csr.signature_algorithm.should == 'sha1WithRSAEncryption'
183
+ end
184
+ it "returns RSA key algorithm for RSA CSR" do
185
+ csr = R509::CSR.new({ :csr => @csr })
186
+ csr.key_algorithm.should == :rsa
187
+ end
188
+ it "returns DSA key algorithm for DSA CSR" do
189
+ csr = R509::CSR.new({ :csr => @csr_dsa })
190
+ csr.key_algorithm.should == :dsa
191
+ end
192
+ it "returns the public key" do
193
+ #this is more complex than it should have to be. diff versions of openssl
194
+ #return subtly diff PEM encodings so we need to look at the modulus (n)
195
+ #but beware, because n is not present for DSA certificates
196
+ csr = R509::CSR.new({ :csr => @csr })
197
+ csr.public_key.n.to_i.should == @csr_public_key_modulus.to_i
198
+ end
199
+ it "returns true with valid signature" do
200
+ csr = R509::CSR.new({ :csr => @csr })
201
+ csr.verify_signature.should == true
202
+ end
203
+ it "returns false on invalid signature" do
204
+ csr = R509::CSR.new({ :csr => @csr_invalid_signature })
205
+ csr.verify_signature.should == false
206
+ end
207
+ it "works when the CSR has unknown OIDs" do
208
+ csr = R509::CSR.new(:csr => @csr_unknown_oid)
209
+ csr.subject["1.2.3.4.5.6.7.8.9.8.7.6.5.4.3.2.1.0.0"].should == "random oid!"
210
+ csr.subject["1.3.3.543.567.32.43.335.1.1.1"].should == "another random oid!"
211
+ end
212
+ end
213
+ context "when supplying a key with csr" do
214
+ it "raises exception on non-matching key" do
215
+ expect { R509::CSR.new({:csr => @csr, :key => @key_csr2}) }.to raise_error(R509::R509Error, 'Key does not match request.')
216
+ end
217
+ it "accepts matching key" do
218
+ csr = R509::CSR.new({:csr => @csr2, :key => @key_csr2})
219
+ csr.to_pem.should == @csr2
220
+ end
221
+ end
222
+ context "when setting alternate signature algorithms" do
223
+ it "sets sha1 if you pass an invalid message digest" do
224
+ csr = R509::CSR.new(:message_digest => 'sha88', :bit_strength => 512, :subject => [['CN','langui.sh']])
225
+ csr.message_digest.name.should == 'sha1'
226
+ csr.signature_algorithm.should == "sha1WithRSAEncryption"
227
+ end
228
+ it "sets sha224 properly" do
229
+ csr = R509::CSR.new(:message_digest => 'sha224', :bit_strength => 512, :subject => [['CN','sha224-signature-alg.test']])
230
+ csr.message_digest.name.should == 'sha224'
231
+ csr.signature_algorithm.should == "sha224WithRSAEncryption"
232
+ end
233
+ it "sets sha256 properly" do
234
+ csr = R509::CSR.new(:message_digest => 'sha256', :bit_strength => 512, :subject => [['CN','sha256-signature-alg.test']])
235
+ csr.message_digest.name.should == 'sha256'
236
+ csr.signature_algorithm.should == "sha256WithRSAEncryption"
237
+ end
238
+ it "sets sha384 properly" do
239
+ csr = R509::CSR.new(:message_digest => 'sha384', :bit_strength => 768, :subject => [['CN','sha384-signature-alg.test']])
240
+ csr.message_digest.name.should == 'sha384'
241
+ csr.signature_algorithm.should == "sha384WithRSAEncryption"
242
+ end
243
+ it "sets sha512 properly" do
244
+ csr = R509::CSR.new(:message_digest => 'sha512', :bit_strength => 1024, :subject => [['CN','sha512-signature-alg.test']])
245
+ csr.message_digest.name.should == 'sha512'
246
+ csr.signature_algorithm.should == "sha512WithRSAEncryption"
247
+ end
248
+ it "sets md5 properly" do
249
+ csr = R509::CSR.new(:message_digest => 'md5', :bit_strength => 512, :subject => [['CN','md5-signature-alg.test']])
250
+ csr.message_digest.name.should == 'md5'
251
+ csr.signature_algorithm.should == "md5WithRSAEncryption"
252
+ end
253
+ end
254
+ it "checks rsa?" do
255
+ csr = R509::CSR.new({:csr => @csr})
256
+ csr.rsa?.should == true
257
+ csr.ec?.should == false
258
+ csr.dsa?.should == false
259
+ end
260
+ it "gets RSA bit strength" do
261
+ csr = R509::CSR.new({:csr => @csr})
262
+ csr.bit_strength.should == 2048
263
+ end
264
+ it "returns an error for curve_name for dsa/rsa CSRs" do
265
+ csr = R509::CSR.new(:csr => @csr)
266
+ expect { csr.curve_name }.to raise_error(R509::R509Error, 'Curve name is only available with EC CSRs')
267
+ end
268
+ it "checks dsa?" do
269
+ csr = R509::CSR.new({:csr => @csr_dsa})
270
+ csr.rsa?.should == false
271
+ csr.ec?.should == false
272
+ csr.dsa?.should == true
273
+ end
274
+ it "gets DSA bit strength" do
275
+ csr = R509::CSR.new({:csr => @csr_dsa})
276
+ csr.bit_strength.should == 1024
277
+ end
278
+
279
+ it "loads a csr with load_from_file" do
280
+ path = File.dirname(__FILE__) + '/fixtures/csr1.pem'
281
+ csr = R509::CSR.load_from_file path
282
+ csr.message_digest.name.should == 'sha1'
283
+ end
284
+
285
+ context "when using elliptic curves", :ec => true do
286
+ it "loads a pre-existing EC CSR" do
287
+ csr = R509::CSR.new(:csr => @ec_csr2_pem)
288
+ csr.to_der.should == @ec_csr2_der
289
+ end
290
+ it "returns the curve name" do
291
+ csr = R509::CSR.new(:csr => @ec_csr2_pem)
292
+ csr.curve_name.should == "secp384r1"
293
+ end
294
+ it "generates a CSR with default curve" do
295
+ csr = R509::CSR.new(:type => :ec, :subject => [["CN","ec-test.local"]])
296
+ csr.curve_name.should == "secp384r1"
297
+ end
298
+ it "generates a CSR with explicit curve" do
299
+ csr = R509::CSR.new(:type => :ec, :curve_name => "sect283r1", :subject => [["CN","ec-test.local"]])
300
+ csr.curve_name.should == "sect283r1"
301
+ end
302
+ it "raises error on bit strength" do
303
+ csr = R509::CSR.new(:csr => @ec_csr2_der)
304
+ expect { csr.bit_strength }.to raise_error(R509::R509Error,'Bit strength is not available for EC at this time.')
305
+ end
306
+ it "returns the key algorithm" do
307
+ csr = R509::CSR.new(:csr => @ec_csr2_pem)
308
+ csr.key_algorithm.should == :ec
309
+ end
310
+ it "returns the public key" do
311
+ csr = R509::CSR.new(:csr => @ec_csr2_pem)
312
+ csr.public_key.private_key?.should == false
313
+ csr.public_key.public_key?.should == true
314
+ end
315
+ it "checks ec?" do
316
+ csr = R509::CSR.new(:csr => @ec_csr2_pem)
317
+ csr.ec?.should == true
318
+ end
319
+ it "sets alternate signature properly for EC" do
320
+ csr = R509::CSR.new(:message_digest => 'sha256', :type => :ec, :subject => [["CN","ec-signature-alg.test"]])
321
+ csr.message_digest.name.should == 'sha256'
322
+ csr.signature_algorithm.should == 'ecdsa-with-SHA256'
323
+ end
324
+ end
325
+
326
+ context "when elliptic curve support is unavailable" do
7
327
  before :all do
8
- @cert = TestFixtures::CERT
9
- @cert_san = TestFixtures::CERT_SAN
10
- @csr = TestFixtures::CSR
11
- @csr_newlines = TestFixtures::CSR_NEWLINES
12
- @csr_no_begin_end = TestFixtures::CSR_NO_BEGIN_END
13
- @csr_der = TestFixtures::CSR_DER
14
- @csr_public_key_modulus = TestFixtures::CSR_PUBLIC_KEY_MODULUS
15
- @csr_invalid_signature = TestFixtures::CSR_INVALID_SIGNATURE
16
- @csr2 = TestFixtures::CSR2
17
- @csr3 = TestFixtures::CSR3
18
- @csr_der = TestFixtures::CSR_DER
19
- @csr_dsa = TestFixtures::CSR_DSA
20
- @csr4_multiple_attrs = TestFixtures::CSR4_MULTIPLE_ATTRS
21
- @key3 = TestFixtures::KEY3
22
- @key_csr2 = TestFixtures::KEY_CSR2
23
- @dsa_key = TestFixtures::DSA_KEY
24
- @csr_unknown_oid = TestFixtures::CSR_UNKNOWN_OID
328
+ @ec = OpenSSL::PKey.send(:remove_const,:EC) # remove EC support for test!
329
+ load('r509/ec-hack.rb')
25
330
  end
26
-
27
- it "raises an exception when passing non-hash" do
28
- expect { R509::Csr.new('invalid') }.to raise_error(ArgumentError, 'Must provide a hash of options')
29
- end
30
- it "key creation defaults to RSA when no type or key is passed" do
31
- csr = R509::Csr.new(:subject => [['CN','testing.rsa']], :bit_strength => 1024)
32
- csr.rsa?.should == true
33
- csr.dsa?.should == false
34
- end
35
- it "returns expected value for to_der" do
36
- csr = R509::Csr.new(:csr => @csr)
37
- csr.to_der.should == @csr_der
38
- end
39
- it "loads a csr with extraneous newlines" do
40
- csr = R509::Csr.new(:csr => @csr_newlines)
41
- csr.to_pem.should match(/-----BEGIN CERTIFICATE REQUEST-----/)
42
- end
43
- it "loads a csr with no begin/end lines" do
44
- csr = R509::Csr.new(:csr => @csr_no_begin_end)
45
- csr.to_pem.should match(/-----BEGIN CERTIFICATE REQUEST-----/)
46
- end
47
- it "returns true from #has_private_key? when private key is present" do
48
- csr = R509::Csr.new(:bit_strength => 512, :subject => [['CN','private-key-check.com']])
49
- csr.has_private_key?.should == true
50
- end
51
- it "returns false from #has_private_key? when private key is not present" do
52
- csr = R509::Csr.new(:csr => @csr)
53
- csr.has_private_key?.should == false
54
- end
55
- it "key creation defaults to 2048 when no bit strength or key is passed" do
56
- csr = R509::Csr.new(:subject => [['CN','testing2048.rsa']])
57
- csr.bit_strength.should == 2048
58
- end
59
- it "creates a CSR when a key is provided" do
60
- csr = R509::Csr.new(:key => @key3, :subject => [['CN','pregenerated.com']], :bit_strength => 1024)
61
- csr.to_pem.should match(/CERTIFICATE REQUEST/)
62
- #validate the CSR matches the key
63
- csr.req.verify(csr.key.public_key).should == true
64
- end
65
- it "loads successfully when an R509::PrivateKey is provided" do
66
- key = R509::PrivateKey.new(:key => @key3)
67
- expect { R509::Csr.new(:key => key, :csr => @csr3)}.to_not raise_error
68
- end
69
- it "raises an exception when you pass a cert and subject" do
70
- expect { R509::Csr.new(:cert => @cert, :subject => [['CN','fail.com']]) }.to raise_error(ArgumentError,'Can only provide one of cert, subject, or csr')
71
- end
72
- it "raises an exception when you pass a cert and CSR" do
73
- expect { R509::Csr.new(:cert => @cert, :csr => @csr) }.to raise_error(ArgumentError,'Can only provide one of cert, subject, or csr')
74
- end
75
- it "raises an exception when you pass a subject and CSR" do
76
- expect { R509::Csr.new(:subject => [['CN','error.com']], :csr => @csr) }.to raise_error(ArgumentError,'Can only provide one of cert, subject, or csr')
77
- end
78
- it "raises an exception for not providing valid type when key is nil" do
79
- expect { R509::Csr.new(:subject => [['CN','error.com']], :type => :invalid_symbol) }.to raise_error(ArgumentError,'Must provide :rsa or :dsa as type when key is nil')
80
- end
81
- it "raises an exception when you don't provide cert, subject, or CSR" do
82
- expect { R509::Csr.new(:bit_strength => 1024) }.to raise_error(ArgumentError,'Must provide one of cert, subject, or csr')
83
- end
84
- it "raises an exception if you provide a list of domains with an existing CSR" do
85
- expect { R509::Csr.new(:csr => @csr, :san_names => ['moredomainsiwanttoadd.com']) }.to raise_error(ArgumentError,'You can\'t add domains to an existing CSR')
86
- end
87
- it "changes the message_digest to DSS1 when passed a DSA key" do
88
- csr = R509::Csr.new(:subject => [["CN","dsasigned.com"]], :key => @dsa_key)
89
- csr.message_digest.name.should == 'dss1'
90
- csr.signature_algorithm.should == 'dsaWithSHA1'
91
- #dss1 is actually the same as SHA1
92
- #Yes this is confusing
93
- #see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKey/DSA.html
94
- end
95
- it "changes the message_digest to DSS1 when creating a DSA key" do
96
- csr = R509::Csr.new(:subject => [["CN","dsasigned.com"]], :type => :dsa, :bit_strength => 512)
97
- csr.message_digest.name.should == 'dss1'
98
- csr.signature_algorithm.should == 'dsaWithSHA1'
99
- #dss1 is actually the same as SHA1
100
- #Yes this is confusing
101
- #see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/PKey/DSA.html
102
- end
103
- it "signs a CSR properly when passed a DSA key" do
104
- csr = R509::Csr.new(:subject => [["CN","dsasigned.com"]], :key => @dsa_key)
105
- csr.verify_signature.should == true
106
- end
107
- it "signs a CSR properly when creating a DSA key" do
108
- csr = R509::Csr.new(:subject => [["CN","dsasigned.com"]], :type => :dsa, :bit_strength => 512)
109
- csr.verify_signature.should == true
110
- end
111
- it "writes to pem" do
112
- csr = R509::Csr.new(:csr => @csr)
113
- sio = StringIO.new
114
- sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
115
- csr.write_pem(sio)
116
- sio.string.should == @csr
117
- end
118
- it "writes to der" do
119
- sio = StringIO.new
120
- sio.set_encoding("BINARY") if sio.respond_to?(:set_encoding)
121
- csr = R509::Csr.new(:csr => @csr)
122
- csr.write_der(sio)
123
- sio.string.should == @csr_der
124
- end
125
- it "duplicate SAN names should be removed" do
126
- csr = R509::Csr.new( :bit_strength => 512, :subject => [['CN','test2345.com']], :san_names => ["test2.local","test.local","test.local"] )
127
- csr.san_names.should == ["test2.local", "test.local"]
128
- end
129
- it "creates a valid hash object with to_hash" do
130
- csr = R509::Csr.new(:csr => @csr)
131
- csr.to_hash[:subject].kind_of?(R509::Subject).should == true
132
- csr.to_hash[:subject].to_s.should == '/CN=test.local/O=Testing CSR'
133
- csr.to_hash[:san_names].should == ["test.local", "additionaldomains.com", "saniam.com"]
134
- end
135
- it "san_names is an empty array when there are no SAN names" do
136
- csr = R509::Csr.new( :subject => [['CN','langui.sh'],['emailAddress','ca@langui.sh']], :bit_strength => 512)
137
- csr.san_names.should == []
138
- end
139
- context "when initialized" do
140
- it "raises exception when providing invalid csr" do
141
- expect { R509::Csr.new({:csr => 'invalid csr'}) }.to raise_error(OpenSSL::X509::RequestError)
142
- end
143
- it "raises exception when providing invalid key" do
144
- expect { R509::Csr.new({:csr => @csr, :key => 'invalid key'}) }.to raise_error(R509::R509Error,"Failed to load private key. Invalid key or incorrect password.")
145
- end
146
- end
147
- context "when passing a cert to generate" do
148
- it "returns a valid pem" do
149
- csr = R509::Csr.new( :bit_strength => 1024, :cert => @cert )
150
- csr.to_pem.should match(/CERTIFICATE REQUEST/)
151
- end
152
- it "has a public key length of 2048 by default" do
153
- csr = R509::Csr.new( :cert => @cert )
154
- csr.bit_strength.should == 2048
155
- end
156
- it "generates a matching CSR" do
157
- csr = R509::Csr.new( :bit_strength => 1024, :cert => @cert )
158
- csr.subject.to_s.should == '/C=US/ST=Illinois/L=Chicago/O=Paul Kehrer/CN=langui.sh'
159
- end
160
- it "SAN domains from the cert should be encoded in the request" do
161
- csr = R509::Csr.new( :bit_strength => 1024, :cert => @cert_san )
162
- csr.san_names.should == ["langui.sh"]
163
- end
164
- it "duplicate SAN names should be removed" do
165
- csr = R509::Csr.new( :cert => @cert, :san_names => ["test2.local","test.local","test.local"] )
166
- csr.san_names.should == ["test2.local", "test.local"]
167
- end
168
- it "SAN names added in addition to those present in the cert should be merged" do
169
- csr = R509::Csr.new( :cert => @cert_san, :san_names => ["test2.local","test.local","test.local"] )
170
- csr.san_names.should == ["langui.sh","test2.local", "test.local"]
171
- end
172
- end
173
- context "when passing a subject array" do
174
- it "generates a matching CSR" do
175
- csr = R509::Csr.new( :subject=> [['CN','langui.sh'],['ST','Illinois'],['L','Chicago'],['C','US'],['emailAddress','ca@langui.sh']], :bit_strength => 1024)
176
- csr.subject.to_s.should == '/CN=langui.sh/ST=Illinois/L=Chicago/C=US/emailAddress=ca@langui.sh'
177
- end
178
- it "adds SAN domains to a generated CSR" do
179
- csr = R509::Csr.new( :subject => [['CN','langui.sh'],['emailAddress','ca@langui.sh']], :bit_strength => 1024, :san_names => ['domain2.com','domain3.com'])
180
- csr.subject.to_s.should == '/CN=langui.sh/emailAddress=ca@langui.sh'
181
- csr.san_names.should == ["domain2.com", "domain3.com"]
182
- end
183
- it "generates a matching csr when supplying raw oids" do
184
- csr = R509::Csr.new( :subject => [['2.5.4.3','common name'],['2.5.4.15','business category'],['2.5.4.7','locality'],['1.3.6.1.4.1.311.60.2.1.3','jurisdiction oid openssl typically does not know']], :bit_strength => 1024 )
185
- csr.subject.to_s.should == "/CN=common name/businessCategory=business category/L=locality/jurisdictionOfIncorporationCountryName=jurisdiction oid openssl typically does not know"
186
- end
187
- end
188
- context "when supplying an existing csr" do
189
- it "populates the bit_strength" do
190
- csr = R509::Csr.new({ :csr => @csr })
191
- csr.bit_strength.should == 2048
192
- end
193
- it "populates the subject" do
194
- csr = R509::Csr.new({ :csr => @csr })
195
- csr.subject.to_s.should == '/CN=test.local/O=Testing CSR'
196
- end
197
- it "parses the san names" do
198
- csr = R509::Csr.new({ :csr => @csr })
199
- csr.san_names.should == ["test.local", "additionaldomains.com", "saniam.com"]
200
- end
201
- it "parses san names when there are multiple non-SAN attributes" do
202
- csr = R509::Csr.new({ :csr => @csr4_multiple_attrs })
203
- csr.san_names.should == ["adomain.com", "anotherdomain.com", "justanexample.com"]
204
- end
205
- it "fetches a subject component" do
206
- csr = R509::Csr.new({ :csr => @csr })
207
- csr.subject_component('CN').to_s.should == 'test.local'
208
- end
209
- it "returns nil when subject component not found" do
210
- csr = R509::Csr.new({ :csr => @csr })
211
- csr.subject_component('OU').should be_nil
212
- end
213
- it "returns the signature algorithm" do
214
- csr = R509::Csr.new({ :csr => @csr })
215
- csr.signature_algorithm.should == 'sha1WithRSAEncryption'
216
- end
217
- it "returns RSA key algorithm for RSA CSR" do
218
- csr = R509::Csr.new({ :csr => @csr })
219
- csr.key_algorithm.should == 'RSA'
220
- end
221
- it "returns DSA key algorithm for DSA CSR" do
222
- csr = R509::Csr.new({ :csr => @csr_dsa })
223
- csr.key_algorithm.should == 'DSA'
224
- end
225
- it "returns the public key" do
226
- #this is more complex than it should have to be. diff versions of openssl
227
- #return subtly diff PEM encodings so we need to look at the modulus (n)
228
- #but beware, because n is not present for DSA certificates
229
- csr = R509::Csr.new({ :csr => @csr })
230
- csr.public_key.n.to_i.should == @csr_public_key_modulus.to_i
231
- end
232
- it "returns true with valid signature" do
233
- csr = R509::Csr.new({ :csr => @csr })
234
- csr.verify_signature.should == true
235
- end
236
- it "returns false on invalid signature" do
237
- csr = R509::Csr.new({ :csr => @csr_invalid_signature })
238
- csr.verify_signature.should == false
239
- end
240
- it "works when the CSR has unknown OIDs" do
241
- csr = R509::Csr.new(:csr => @csr_unknown_oid)
242
- csr.subject["1.2.3.4.5.6.7.8.9.8.7.6.5.4.3.2.1.0.0"].should == "random oid!"
243
- csr.subject["1.3.3.543.567.32.43.335.1.1.1"].should == "another random oid!"
244
- end
245
- end
246
- context "when supplying a key with csr" do
247
- it "raises exception on non-matching key" do
248
- expect { R509::Csr.new({:csr => @csr, :key => @key_csr2}) }.to raise_error(R509::R509Error, 'Key does not match request.')
249
- end
250
- it "accepts matching key" do
251
- csr = R509::Csr.new({:csr => @csr2, :key => @key_csr2})
252
- csr.to_pem.should == @csr2
253
- end
254
- end
255
- context "when setting alternate signature algorithms" do
256
- it "sets sha1 if you pass an invalid message digest" do
257
- csr = R509::Csr.new(:message_digest => 'sha88', :bit_strength => 512, :subject => [['CN','langui.sh']])
258
- csr.message_digest.name.should == 'sha1'
259
- csr.signature_algorithm.should == "sha1WithRSAEncryption"
260
- end
261
- it "sets sha256 properly" do
262
- csr = R509::Csr.new(:message_digest => 'sha256', :bit_strength => 512, :subject => [['CN','sha256-signature-alg.test']])
263
- csr.message_digest.name.should == 'sha256'
264
- csr.signature_algorithm.should == "sha256WithRSAEncryption"
265
- end
266
- it "sets sha512 properly" do
267
- csr = R509::Csr.new(:message_digest => 'sha512', :bit_strength => 1024, :subject => [['CN','sha512-signature-alg.test']])
268
- csr.message_digest.name.should == 'sha512'
269
- csr.signature_algorithm.should == "sha512WithRSAEncryption"
270
- end
271
- it "sets md5 properly" do
272
- csr = R509::Csr.new(:message_digest => 'md5', :bit_strength => 512, :subject => [['CN','md5-signature-alg.test']])
273
- csr.message_digest.name.should == 'md5'
274
- csr.signature_algorithm.should == "md5WithRSAEncryption"
275
- end
331
+ after :all do
332
+ OpenSSL::PKey.send(:remove_const,:EC) # remove stubbed EC
333
+ OpenSSL::PKey::EC = @ec # add the real one back
276
334
  end
277
335
  it "checks rsa?" do
278
- csr = R509::Csr.new({:csr => @csr})
279
- csr.rsa?.should == true
280
- csr.dsa?.should == false
336
+ csr = R509::CSR.new({:csr => @csr})
337
+ csr.rsa?.should == true
338
+ csr.ec?.should == false
339
+ csr.dsa?.should == false
281
340
  end
282
- it "gets RSA bit strength" do
283
- csr = R509::Csr.new({:csr => @csr})
284
- csr.bit_strength.should == 2048
285
- end
286
- it "checks dsa?" do
287
- csr = R509::Csr.new({:csr => @csr_dsa})
288
- csr.rsa?.should == false
289
- csr.dsa?.should == true
290
- end
291
- it "gets DSA bit strength" do
292
- csr = R509::Csr.new({:csr => @csr_dsa})
293
- csr.bit_strength.should == 1024
294
- end
295
-
296
- it "loads a csr with load_from_file" do
297
- path = File.dirname(__FILE__) + '/fixtures/csr1.pem'
298
- csr = R509::Csr.load_from_file path
299
- csr.message_digest.name.should == 'sha1'
341
+ it "returns RSA key algorithm for RSA CSR" do
342
+ csr = R509::CSR.new({ :csr => @csr })
343
+ csr.key_algorithm.should == :rsa
300
344
  end
345
+ end
301
346
 
302
347
  end