r509 0.8.1 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +343 -151
- data/Rakefile +26 -23
- data/bin/r509 +126 -112
- data/bin/r509-parse +24 -24
- data/doc/R509.html +169 -7
- data/doc/R509/ASN1.html +370 -0
- data/doc/R509/ASN1/GeneralName.html +1121 -0
- data/doc/R509/ASN1/GeneralNames.html +843 -0
- data/doc/R509/ASN1/NoticeReference.html +392 -0
- data/doc/R509/ASN1/PolicyInformation.html +387 -0
- data/doc/R509/ASN1/PolicyQualifiers.html +455 -0
- data/doc/R509/ASN1/UserNotice.html +386 -0
- data/doc/R509/{Crl.html → CRL.html} +7 -7
- data/doc/R509/CRL/Administrator.html +1559 -0
- data/doc/R509/{Crl/Parser.html → CRL/SignedList.html} +501 -210
- data/doc/R509/{Csr.html → CSR.html} +444 -314
- data/doc/R509/Cert.html +866 -617
- data/doc/R509/Cert/Extensions.html +52 -41
- data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +70 -35
- data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +387 -4
- data/doc/R509/Cert/Extensions/BasicConstraints.html +61 -25
- data/doc/R509/Cert/Extensions/CRLDistributionPoints.html +354 -0
- data/doc/R509/Cert/Extensions/CertificatePolicies.html +340 -0
- data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +440 -49
- data/doc/R509/Cert/Extensions/{CrlDistributionPoints.html → InhibitAnyPolicy.html} +52 -35
- data/doc/R509/Cert/Extensions/KeyUsage.html +247 -121
- data/doc/R509/Cert/Extensions/NameConstraints.html +445 -0
- data/doc/R509/Cert/Extensions/OCSPNoCheck.html +239 -0
- data/doc/R509/Cert/Extensions/PolicyConstraints.html +424 -0
- data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +437 -62
- data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +52 -10
- data/doc/R509/CertificateAuthority.html +4 -4
- data/doc/R509/CertificateAuthority/Signer.html +154 -187
- data/doc/R509/Config.html +6 -6
- data/doc/R509/Config/{CaConfig.html → CAConfig.html} +451 -348
- data/doc/R509/Config/{CaConfigPool.html → CAConfigPool.html} +47 -47
- data/doc/R509/Config/CAProfile.html +1015 -0
- data/doc/R509/Config/SubjectItemPolicy.html +86 -86
- data/doc/R509/IOHelpers.html +22 -22
- data/doc/R509/MessageDigest.html +14 -14
- data/doc/R509/NameSanitizer.html +53 -53
- data/doc/R509/{Ocsp.html → OCSP.html} +9 -9
- data/doc/R509/{Ocsp → OCSP}/Request.html +7 -7
- data/doc/R509/{Ocsp → OCSP}/Request/Nonce.html +56 -11
- data/doc/R509/{Ocsp → OCSP}/Response.html +44 -44
- data/doc/R509/{OidMapper.html → OIDMapper.html} +23 -39
- data/doc/R509/PrivateKey.html +415 -168
- data/doc/R509/R509Error.html +3 -3
- data/doc/R509/{Spki.html → SPKI.html} +354 -192
- data/doc/R509/Subject.html +224 -113
- data/doc/R509/Validity.html +27 -5
- data/doc/R509/Validity/Checker.html +13 -13
- data/doc/R509/Validity/DefaultChecker.html +13 -13
- data/doc/R509/Validity/DefaultWriter.html +14 -14
- data/doc/R509/Validity/Status.html +39 -39
- data/doc/R509/Validity/Writer.html +18 -18
- data/doc/_index.html +138 -35
- data/doc/class_list.html +1 -1
- data/doc/css/style.css +10 -0
- data/doc/file.README.html +368 -171
- data/doc/file.r509.html +92 -69
- data/doc/frames.html +1 -1
- data/doc/index.html +368 -171
- data/doc/method_list.html +910 -390
- data/doc/top-level-namespace.html +3 -3
- data/lib/r509.rb +32 -16
- data/lib/r509/asn1.rb +375 -0
- data/lib/r509/cert.rb +381 -364
- data/lib/r509/cert/extensions.rb +443 -76
- data/lib/r509/certificate_authority.rb +407 -0
- data/lib/r509/config.rb +547 -351
- data/lib/r509/crl.rb +336 -366
- data/lib/r509/csr.rb +278 -289
- data/lib/r509/ec-hack.rb +37 -0
- data/lib/r509/exceptions.rb +3 -3
- data/lib/r509/io_helpers.rb +44 -44
- data/lib/r509/message_digest.rb +53 -0
- data/lib/r509/ocsp.rb +80 -70
- data/lib/r509/oid_mapper.rb +32 -0
- data/lib/r509/private_key.rb +228 -0
- data/lib/r509/spki.rb +145 -93
- data/lib/r509/subject.rb +203 -110
- data/lib/r509/validity.rb +70 -68
- data/lib/r509/version.rb +2 -2
- data/r509.yaml +92 -69
- data/spec/asn1_spec.rb +402 -0
- data/spec/cert/extensions_spec.rb +957 -494
- data/spec/cert_spec.rb +382 -307
- data/spec/certificate_authority_spec.rb +668 -250
- data/spec/config_spec.rb +515 -302
- data/spec/crl_spec.rb +197 -198
- data/spec/csr_spec.rb +334 -289
- data/spec/fixtures.rb +247 -171
- data/spec/fixtures/cert1.der +0 -0
- data/spec/fixtures/cert1.pem +0 -0
- data/spec/fixtures/cert1_public_key_modulus.txt +0 -0
- data/spec/fixtures/cert3.p12 +0 -0
- data/spec/fixtures/cert3.pem +0 -0
- data/spec/fixtures/cert3_key.pem +0 -0
- data/spec/fixtures/cert3_key_des3.pem +0 -0
- data/spec/fixtures/cert4.pem +0 -0
- data/spec/fixtures/cert5.pem +0 -0
- data/spec/fixtures/cert6.pem +0 -0
- data/spec/fixtures/cert_expired.pem +0 -0
- data/spec/fixtures/cert_inhibit.pem +24 -0
- data/spec/fixtures/cert_name_constraints.pem +29 -0
- data/spec/fixtures/cert_not_yet_valid.pem +0 -0
- data/spec/fixtures/cert_ocsp_no_check.pem +18 -0
- data/spec/fixtures/cert_policy_constraints.pem +31 -0
- data/spec/fixtures/cert_san.pem +0 -0
- data/spec/fixtures/cert_san2.pem +0 -0
- data/spec/fixtures/cert_unknown_extension.pem +28 -0
- data/spec/fixtures/config_pool_test_minimal.yaml +11 -11
- data/spec/fixtures/config_test.yaml +54 -36
- data/spec/fixtures/config_test_dsa.yaml +35 -0
- data/spec/fixtures/config_test_ec.yaml +35 -0
- data/spec/fixtures/config_test_engine_key.yaml +5 -5
- data/spec/fixtures/config_test_engine_no_key_name.yaml +4 -4
- data/spec/fixtures/config_test_minimal.yaml +4 -4
- data/spec/fixtures/config_test_password.yaml +5 -5
- data/spec/fixtures/config_test_various.yaml +111 -74
- data/spec/fixtures/crl_list_file.txt +0 -0
- data/spec/fixtures/crl_with_reason.pem +0 -0
- data/spec/fixtures/csr1.der +0 -0
- data/spec/fixtures/csr1.pem +0 -0
- data/spec/fixtures/csr1_key.der +0 -0
- data/spec/fixtures/csr1_key.pem +0 -0
- data/spec/fixtures/csr1_key_encrypted_des3.pem +0 -0
- data/spec/fixtures/csr1_newlines.pem +0 -0
- data/spec/fixtures/csr1_no_begin_end.pem +0 -0
- data/spec/fixtures/csr1_public_key_modulus.txt +0 -0
- data/spec/fixtures/csr2.pem +0 -0
- data/spec/fixtures/csr2_key.pem +0 -0
- data/spec/fixtures/csr3.pem +0 -0
- data/spec/fixtures/csr4.pem +0 -0
- data/spec/fixtures/csr_dsa.pem +0 -0
- data/spec/fixtures/csr_invalid_signature.pem +0 -0
- data/spec/fixtures/dsa_key.pem +0 -0
- data/spec/fixtures/dsa_root.cer +28 -0
- data/spec/fixtures/dsa_root.key +20 -0
- data/spec/fixtures/ec_csr2.der +0 -0
- data/spec/fixtures/ec_csr2.pem +8 -0
- data/spec/fixtures/ec_key1.der +0 -0
- data/spec/fixtures/ec_key1.pem +6 -0
- data/spec/fixtures/ec_key1_encrypted.pem +9 -0
- data/spec/fixtures/ec_key2.pem +6 -0
- data/spec/fixtures/hmacsha1.sig +1 -0
- data/spec/fixtures/hmacsha512.sig +1 -0
- data/spec/fixtures/key4.pem +0 -0
- data/spec/fixtures/key4_encrypted_des3.pem +0 -0
- data/spec/fixtures/missing_key_identifier_ca.cer +0 -0
- data/spec/fixtures/missing_key_identifier_ca.key +0 -0
- data/spec/fixtures/ocsptest.r509.local.pem +0 -0
- data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
- data/spec/fixtures/ocsptest2.r509.local.pem +0 -0
- data/spec/fixtures/second_ca.cer +0 -0
- data/spec/fixtures/second_ca.key +0 -0
- data/spec/fixtures/spkac.der +0 -0
- data/spec/fixtures/spkac.txt +0 -0
- data/spec/fixtures/spkac_dsa.txt +1 -1
- data/spec/fixtures/spkac_dsa_no_verify.txt +1 -0
- data/spec/fixtures/spkac_ec.txt +1 -0
- data/spec/fixtures/spkac_rsa_newlines.txt +13 -0
- data/spec/fixtures/stca.pem +0 -0
- data/spec/fixtures/stca_ocsp_request.der +0 -0
- data/spec/fixtures/stca_ocsp_response.der +0 -0
- data/spec/fixtures/test1.csr +0 -0
- data/spec/fixtures/test_ca.cer +0 -0
- data/spec/fixtures/test_ca.key +0 -0
- data/spec/fixtures/test_ca.p12 +0 -0
- data/spec/fixtures/test_ca_des3.key +0 -0
- data/spec/fixtures/test_ca_ec.cer +14 -0
- data/spec/fixtures/test_ca_ec.key +6 -0
- data/spec/fixtures/test_ca_ec_ee.cer +22 -0
- data/spec/fixtures/test_ca_ec_ee.key +6 -0
- data/spec/fixtures/test_ca_ocsp.cer +0 -0
- data/spec/fixtures/test_ca_ocsp.key +0 -0
- data/spec/fixtures/test_ca_ocsp.p12 +0 -0
- data/spec/fixtures/test_ca_ocsp_chain.txt +0 -0
- data/spec/fixtures/test_ca_ocsp_response.der +0 -0
- data/spec/fixtures/test_ca_subroot.cer +0 -0
- data/spec/fixtures/test_ca_subroot.key +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp.cer +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp.key +0 -0
- data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
- data/spec/fixtures/unknown_oid.csr +0 -0
- data/spec/message_digest_spec.rb +104 -84
- data/spec/ocsp_spec.rb +105 -105
- data/spec/oid_mapper_spec.rb +21 -21
- data/spec/private_key_spec.rb +275 -0
- data/spec/r509_spec.rb +35 -0
- data/spec/spec_helper.rb +15 -6
- data/spec/spki_spec.rb +221 -142
- data/spec/subject_spec.rb +232 -164
- data/spec/validity_spec.rb +91 -91
- metadata +79 -25
- data/doc/R509/Config/CaProfile.html +0 -651
- data/doc/R509/Crl/Administrator.html +0 -2073
- data/lib/r509/certificateauthority.rb +0 -290
- data/lib/r509/messagedigest.rb +0 -49
- data/lib/r509/oidmapper.rb +0 -32
- data/lib/r509/privatekey.rb +0 -185
- data/spec/privatekey_spec.rb +0 -198
data/spec/crl_spec.rb
CHANGED
@@ -1,215 +1,214 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'stringio'
|
3
3
|
|
4
|
-
describe R509::
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
21
|
+
it "returns last_update" do
|
22
|
+
@crl.last_update.should == Time.at(1327446093)
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
it "returns next_update" do
|
26
|
+
@crl.next_update.should == Time.at(1328054493)
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
it "returns signature_algorithm" do
|
30
|
+
@crl.signature_algorithm.should == "sha1WithRSAEncryption"
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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::
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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::
|
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
|
-
|
9
|
-
|
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
|
-
|
28
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
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 "
|
283
|
-
|
284
|
-
|
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
|