r509 0.8.1 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
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