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