r509 0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +447 -0
- data/Rakefile +38 -0
- data/bin/r509 +96 -0
- data/bin/r509-parse +35 -0
- data/doc/R509.html +154 -0
- data/doc/R509/Cert.html +3954 -0
- data/doc/R509/Cert/Extensions.html +360 -0
- data/doc/R509/Cert/Extensions/AuthorityInfoAccess.html +391 -0
- data/doc/R509/Cert/Extensions/AuthorityKeyIdentifier.html +148 -0
- data/doc/R509/Cert/Extensions/BasicConstraints.html +482 -0
- data/doc/R509/Cert/Extensions/CrlDistributionPoints.html +316 -0
- data/doc/R509/Cert/Extensions/ExtendedKeyUsage.html +780 -0
- data/doc/R509/Cert/Extensions/KeyUsage.html +1230 -0
- data/doc/R509/Cert/Extensions/SubjectAlternativeName.html +467 -0
- data/doc/R509/Cert/Extensions/SubjectKeyIdentifier.html +216 -0
- data/doc/R509/CertificateAuthority.html +126 -0
- data/doc/R509/CertificateAuthority/Signer.html +855 -0
- data/doc/R509/Config.html +127 -0
- data/doc/R509/Config/CaConfig.html +2144 -0
- data/doc/R509/Config/CaConfigPool.html +599 -0
- data/doc/R509/Config/CaProfile.html +656 -0
- data/doc/R509/Config/SubjectItemPolicy.html +578 -0
- data/doc/R509/Crl.html +126 -0
- data/doc/R509/Crl/Administrator.html +2077 -0
- data/doc/R509/Crl/Parser.html +1224 -0
- data/doc/R509/Csr.html +2248 -0
- data/doc/R509/IOHelpers.html +564 -0
- data/doc/R509/MessageDigest.html +396 -0
- data/doc/R509/NameSanitizer.html +319 -0
- data/doc/R509/Ocsp.html +128 -0
- data/doc/R509/Ocsp/Request.html +126 -0
- data/doc/R509/Ocsp/Request/Nonce.html +160 -0
- data/doc/R509/Ocsp/Response.html +837 -0
- data/doc/R509/OidMapper.html +393 -0
- data/doc/R509/PrivateKey.html +1647 -0
- data/doc/R509/R509Error.html +134 -0
- data/doc/R509/Spki.html +1424 -0
- data/doc/R509/Subject.html +836 -0
- data/doc/R509/Validity.html +160 -0
- data/doc/R509/Validity/Checker.html +320 -0
- data/doc/R509/Validity/DefaultChecker.html +283 -0
- data/doc/R509/Validity/DefaultWriter.html +330 -0
- data/doc/R509/Validity/Status.html +561 -0
- data/doc/R509/Validity/Writer.html +394 -0
- data/doc/_index.html +501 -0
- data/doc/class_list.html +53 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +328 -0
- data/doc/file.README.html +534 -0
- data/doc/file.r509.html +149 -0
- data/doc/file_list.html +58 -0
- data/doc/frames.html +28 -0
- data/doc/index.html +534 -0
- data/doc/js/app.js +208 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +4 -0
- data/doc/methods_list.html +1932 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/r509.rb +22 -0
- data/lib/r509/cert.rb +414 -0
- data/lib/r509/cert/extensions.rb +309 -0
- data/lib/r509/certificateauthority.rb +290 -0
- data/lib/r509/config.rb +407 -0
- data/lib/r509/crl.rb +379 -0
- data/lib/r509/csr.rb +324 -0
- data/lib/r509/exceptions.rb +5 -0
- data/lib/r509/io_helpers.rb +52 -0
- data/lib/r509/messagedigest.rb +49 -0
- data/lib/r509/ocsp.rb +85 -0
- data/lib/r509/oidmapper.rb +32 -0
- data/lib/r509/privatekey.rb +185 -0
- data/lib/r509/spki.rb +112 -0
- data/lib/r509/subject.rb +133 -0
- data/lib/r509/validity.rb +92 -0
- data/lib/r509/version.rb +4 -0
- data/r509.yaml +73 -0
- data/spec/cert/extensions_spec.rb +632 -0
- data/spec/cert_spec.rb +321 -0
- data/spec/certificate_authority_spec.rb +260 -0
- data/spec/config_spec.rb +349 -0
- data/spec/crl_spec.rb +215 -0
- data/spec/csr_spec.rb +302 -0
- data/spec/fixtures.rb +233 -0
- data/spec/fixtures/cert1.der +0 -0
- data/spec/fixtures/cert1.pem +24 -0
- data/spec/fixtures/cert1_public_key_modulus.txt +1 -0
- data/spec/fixtures/cert3.p12 +0 -0
- data/spec/fixtures/cert3.pem +28 -0
- data/spec/fixtures/cert3_key.pem +27 -0
- data/spec/fixtures/cert3_key_des3.pem +30 -0
- data/spec/fixtures/cert4.pem +14 -0
- data/spec/fixtures/cert5.pem +30 -0
- data/spec/fixtures/cert6.pem +26 -0
- data/spec/fixtures/cert_expired.pem +26 -0
- data/spec/fixtures/cert_not_yet_valid.pem +26 -0
- data/spec/fixtures/cert_san.pem +27 -0
- data/spec/fixtures/cert_san2.pem +22 -0
- data/spec/fixtures/config_pool_test_minimal.yaml +15 -0
- data/spec/fixtures/config_test.yaml +41 -0
- data/spec/fixtures/config_test_engine_key.yaml +7 -0
- data/spec/fixtures/config_test_engine_no_key_name.yaml +6 -0
- data/spec/fixtures/config_test_minimal.yaml +7 -0
- data/spec/fixtures/config_test_password.yaml +7 -0
- data/spec/fixtures/config_test_various.yaml +100 -0
- data/spec/fixtures/crl_list_file.txt +1 -0
- data/spec/fixtures/crl_with_reason.pem +17 -0
- data/spec/fixtures/csr1.der +0 -0
- data/spec/fixtures/csr1.pem +17 -0
- data/spec/fixtures/csr1_key.der +0 -0
- data/spec/fixtures/csr1_key.pem +27 -0
- data/spec/fixtures/csr1_key_encrypted_des3.pem +30 -0
- data/spec/fixtures/csr1_newlines.pem +32 -0
- data/spec/fixtures/csr1_no_begin_end.pem +15 -0
- data/spec/fixtures/csr1_public_key_modulus.txt +1 -0
- data/spec/fixtures/csr2.pem +15 -0
- data/spec/fixtures/csr2_key.pem +27 -0
- data/spec/fixtures/csr3.pem +16 -0
- data/spec/fixtures/csr4.pem +25 -0
- data/spec/fixtures/csr_dsa.pem +15 -0
- data/spec/fixtures/csr_invalid_signature.pem +13 -0
- data/spec/fixtures/dsa_key.pem +20 -0
- data/spec/fixtures/key4.pem +27 -0
- data/spec/fixtures/key4_encrypted_des3.pem +30 -0
- data/spec/fixtures/missing_key_identifier_ca.cer +21 -0
- data/spec/fixtures/missing_key_identifier_ca.key +27 -0
- data/spec/fixtures/ocsptest.r509.local.pem +27 -0
- data/spec/fixtures/ocsptest.r509.local_ocsp_request.der +0 -0
- data/spec/fixtures/ocsptest2.r509.local.pem +27 -0
- data/spec/fixtures/second_ca.cer +26 -0
- data/spec/fixtures/second_ca.key +27 -0
- data/spec/fixtures/spkac.der +0 -0
- data/spec/fixtures/spkac.txt +1 -0
- data/spec/fixtures/spkac_dsa.txt +1 -0
- data/spec/fixtures/stca.pem +22 -0
- data/spec/fixtures/stca_ocsp_request.der +0 -0
- data/spec/fixtures/stca_ocsp_response.der +0 -0
- data/spec/fixtures/test1.csr +17 -0
- data/spec/fixtures/test_ca.cer +22 -0
- data/spec/fixtures/test_ca.key +28 -0
- data/spec/fixtures/test_ca.p12 +0 -0
- data/spec/fixtures/test_ca_des3.key +30 -0
- data/spec/fixtures/test_ca_ocsp.cer +26 -0
- data/spec/fixtures/test_ca_ocsp.key +27 -0
- data/spec/fixtures/test_ca_ocsp.p12 +0 -0
- data/spec/fixtures/test_ca_ocsp_chain.txt +48 -0
- data/spec/fixtures/test_ca_ocsp_response.der +0 -0
- data/spec/fixtures/test_ca_subroot.cer +26 -0
- data/spec/fixtures/test_ca_subroot.key +27 -0
- data/spec/fixtures/test_ca_subroot_ocsp.cer +25 -0
- data/spec/fixtures/test_ca_subroot_ocsp.key +27 -0
- data/spec/fixtures/test_ca_subroot_ocsp_response.der +0 -0
- data/spec/fixtures/unknown_oid.csr +17 -0
- data/spec/message_digest_spec.rb +89 -0
- data/spec/ocsp_spec.rb +111 -0
- data/spec/oid_mapper_spec.rb +31 -0
- data/spec/privatekey_spec.rb +198 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/spki_spec.rb +157 -0
- data/spec/subject_spec.rb +203 -0
- data/spec/validity_spec.rb +98 -0
- metadata +257 -0
data/spec/csr_spec.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
require 'r509/csr'
|
4
|
+
|
5
|
+
|
6
|
+
describe R509::Csr do
|
7
|
+
before :all do
|
8
|
+
@cert = TestFixtures::CERT
|
9
|
+
@cert_san = TestFixtures::CERT_SAN
|
10
|
+
@csr = TestFixtures::CSR
|
11
|
+
@csr_newlines = TestFixtures::CSR_NEWLINES
|
12
|
+
@csr_no_begin_end = TestFixtures::CSR_NO_BEGIN_END
|
13
|
+
@csr_der = TestFixtures::CSR_DER
|
14
|
+
@csr_public_key_modulus = TestFixtures::CSR_PUBLIC_KEY_MODULUS
|
15
|
+
@csr_invalid_signature = TestFixtures::CSR_INVALID_SIGNATURE
|
16
|
+
@csr2 = TestFixtures::CSR2
|
17
|
+
@csr3 = TestFixtures::CSR3
|
18
|
+
@csr_der = TestFixtures::CSR_DER
|
19
|
+
@csr_dsa = TestFixtures::CSR_DSA
|
20
|
+
@csr4_multiple_attrs = TestFixtures::CSR4_MULTIPLE_ATTRS
|
21
|
+
@key3 = TestFixtures::KEY3
|
22
|
+
@key_csr2 = TestFixtures::KEY_CSR2
|
23
|
+
@dsa_key = TestFixtures::DSA_KEY
|
24
|
+
@csr_unknown_oid = TestFixtures::CSR_UNKNOWN_OID
|
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
|
+
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
|
276
|
+
end
|
277
|
+
it "checks rsa?" do
|
278
|
+
csr = R509::Csr.new({:csr => @csr})
|
279
|
+
csr.rsa?.should == true
|
280
|
+
csr.dsa?.should == false
|
281
|
+
end
|
282
|
+
it "gets RSA bit strength" do
|
283
|
+
csr = R509::Csr.new({:csr => @csr})
|
284
|
+
csr.bit_strength.should == 2048
|
285
|
+
end
|
286
|
+
it "checks dsa?" do
|
287
|
+
csr = R509::Csr.new({:csr => @csr_dsa})
|
288
|
+
csr.rsa?.should == false
|
289
|
+
csr.dsa?.should == true
|
290
|
+
end
|
291
|
+
it "gets DSA bit strength" do
|
292
|
+
csr = R509::Csr.new({:csr => @csr_dsa})
|
293
|
+
csr.bit_strength.should == 1024
|
294
|
+
end
|
295
|
+
|
296
|
+
it "loads a csr with load_from_file" do
|
297
|
+
path = File.dirname(__FILE__) + '/fixtures/csr1.pem'
|
298
|
+
csr = R509::Csr.load_from_file path
|
299
|
+
csr.message_digest.name.should == 'sha1'
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
data/spec/fixtures.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pathname'
|
3
|
+
require 'r509/io_helpers'
|
4
|
+
|
5
|
+
module TestFixtures
|
6
|
+
extend R509::IOHelpers
|
7
|
+
|
8
|
+
FIXTURES_PATH = Pathname.new(__FILE__).dirname + "fixtures"
|
9
|
+
|
10
|
+
def self.read_fixture(filename)
|
11
|
+
read_data((FIXTURES_PATH + filename).to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
#Trustwave cert for langui.sh
|
15
|
+
CERT = read_fixture('cert1.pem')
|
16
|
+
|
17
|
+
#Trustwave root cert
|
18
|
+
STCA_CERT = read_fixture('stca.pem')
|
19
|
+
|
20
|
+
CERT_PUBLIC_KEY_MODULUS = read_fixture('cert1_public_key_modulus.txt')
|
21
|
+
|
22
|
+
# cert without key usage
|
23
|
+
CERT4 = read_fixture('cert4.pem')
|
24
|
+
|
25
|
+
# cert with multiple EKU
|
26
|
+
CERT5 = read_fixture('cert5.pem')
|
27
|
+
|
28
|
+
# cert with DSA public key
|
29
|
+
CERT6 = read_fixture('cert6.pem')
|
30
|
+
|
31
|
+
CERT_EXPIRED = read_fixture("cert_expired.pem")
|
32
|
+
|
33
|
+
CERT_NOT_YET_VALID = read_fixture("cert_not_yet_valid.pem")
|
34
|
+
|
35
|
+
DSA_KEY = read_fixture('dsa_key.pem')
|
36
|
+
|
37
|
+
# this CSR has unknown OIDs, which we should successfully parse out into Subject
|
38
|
+
CSR_UNKNOWN_OID = read_fixture('unknown_oid.csr')
|
39
|
+
|
40
|
+
|
41
|
+
#san cert from self-signed CA for langui.sh
|
42
|
+
CERT_SAN = read_fixture('cert_san.pem')
|
43
|
+
|
44
|
+
#Another san cert for langui.sh, but differentiating between the CN and
|
45
|
+
# SANs.
|
46
|
+
CERT_SAN2 = read_fixture('cert_san2.pem')
|
47
|
+
|
48
|
+
CERT_DER = read_fixture('cert1.der')
|
49
|
+
|
50
|
+
SPKI = read_fixture('spkac.txt')
|
51
|
+
|
52
|
+
SPKI_DER = read_fixture('spkac.der')
|
53
|
+
|
54
|
+
SPKI_DSA = read_fixture('spkac_dsa.txt')
|
55
|
+
|
56
|
+
CSR = read_fixture('csr1.pem')
|
57
|
+
|
58
|
+
CSR_PUBLIC_KEY_MODULUS = read_fixture('csr1_public_key_modulus.txt')
|
59
|
+
|
60
|
+
CSR_INVALID_SIGNATURE = read_fixture('csr_invalid_signature.pem')
|
61
|
+
|
62
|
+
CSR_DER = read_fixture('csr1.der')
|
63
|
+
|
64
|
+
CSR_NEWLINES = read_fixture('csr1_newlines.pem')
|
65
|
+
|
66
|
+
CSR_NO_BEGIN_END = read_fixture('csr1_no_begin_end.pem')
|
67
|
+
|
68
|
+
CSR_DSA = read_fixture('csr_dsa.pem')
|
69
|
+
|
70
|
+
KEY_CSR = read_fixture('csr1_key.pem')
|
71
|
+
|
72
|
+
KEY_CSR_DER = read_fixture('csr1_key.der')
|
73
|
+
|
74
|
+
KEY_CSR_ENCRYPTED = read_fixture('csr1_key_encrypted_des3.pem')
|
75
|
+
|
76
|
+
CSR2 = read_fixture('csr2.pem')
|
77
|
+
|
78
|
+
KEY_CSR2 = read_fixture('csr2_key.pem')
|
79
|
+
|
80
|
+
CSR3 = read_fixture('csr3.pem')
|
81
|
+
|
82
|
+
CERT3 = read_fixture('cert3.pem')
|
83
|
+
|
84
|
+
KEY3 = read_fixture('cert3_key.pem')
|
85
|
+
|
86
|
+
KEY3_ENCRYPTED = read_fixture('cert3_key_des3.pem')
|
87
|
+
|
88
|
+
CERT3_P12 = read_fixture('cert3.p12')
|
89
|
+
|
90
|
+
CSR4_MULTIPLE_ATTRS = read_fixture('csr4.pem')
|
91
|
+
|
92
|
+
KEY4_ENCRYPTED_DES3 = read_fixture('key4_encrypted_des3.pem')
|
93
|
+
|
94
|
+
KEY4 = read_fixture('key4.pem')
|
95
|
+
|
96
|
+
TEST_CA_CERT = read_fixture('test_ca.cer')
|
97
|
+
TEST_CA_KEY = read_fixture('test_ca.key')
|
98
|
+
|
99
|
+
TEST_CA_OCSP_CERT = read_fixture('test_ca_ocsp.cer')
|
100
|
+
TEST_CA_OCSP_KEY = read_fixture('test_ca_ocsp.key')
|
101
|
+
|
102
|
+
TEST_CA_SUBROOT_CERT = read_fixture('test_ca_subroot.cer')
|
103
|
+
TEST_CA_SUBROOT_KEY = read_fixture('test_ca_subroot.key')
|
104
|
+
|
105
|
+
#this chain contains 2 certs. root and OCSP delegate
|
106
|
+
#in a prod environment you'd really only need the delegate
|
107
|
+
#since the root would be present in the root store of the
|
108
|
+
#client, but I wanted to test > 1
|
109
|
+
TEST_CA_OCSP_CHAIN = read_fixture('test_ca_ocsp_chain.txt')
|
110
|
+
|
111
|
+
TEST_CA_OCSP_RESPONSE = read_fixture('test_ca_ocsp_response.der')
|
112
|
+
|
113
|
+
TEST_CA_SUBROOT_OCSP_RESPONSE = read_fixture('test_ca_subroot_ocsp_response.der')
|
114
|
+
|
115
|
+
SECOND_CA_CERT = read_fixture('second_ca.cer')
|
116
|
+
SECOND_CA_KEY = read_fixture('second_ca.key')
|
117
|
+
|
118
|
+
OCSP_TEST_CERT = read_fixture('ocsptest.r509.local.pem')
|
119
|
+
OCSP_TEST_CERT2 = read_fixture('ocsptest2.r509.local.pem')
|
120
|
+
|
121
|
+
STCA_OCSP_REQUEST = read_fixture('stca_ocsp_request.der')
|
122
|
+
STCA_OCSP_RESPONSE = read_fixture('stca_ocsp_response.der')
|
123
|
+
|
124
|
+
CRL_LIST_FILE = (FIXTURES_PATH+'crl_list_file.txt').to_s
|
125
|
+
|
126
|
+
CRL_REASON = read_fixture("crl_with_reason.pem")
|
127
|
+
|
128
|
+
def self.test_ca_cert
|
129
|
+
R509::Cert.new(:cert => TEST_CA_CERT, :key => TEST_CA_KEY)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.test_ca_subroot_cert
|
133
|
+
R509::Cert.new(:cert => TEST_CA_SUBROOT_CERT, :key => TEST_CA_SUBROOT_KEY)
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.test_ca_server_profile
|
137
|
+
R509::Config::CaProfile.new(
|
138
|
+
:basic_constraints => "CA:FALSE",
|
139
|
+
:key_usage => ["digitalSignature","keyEncipherment"],
|
140
|
+
:extended_key_usage => ["serverAuth"],
|
141
|
+
:certificate_policies => [
|
142
|
+
[
|
143
|
+
"policyIdentifier=2.16.840.1.12345.1.2.3.4.1",
|
144
|
+
"CPS.1=http://example.com/cps"
|
145
|
+
]
|
146
|
+
]
|
147
|
+
)
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.test_ca_server_profile_with_subject_item_policy
|
152
|
+
subject_item_policy = R509::Config::SubjectItemPolicy.new(
|
153
|
+
"CN" => "required",
|
154
|
+
"O" => "optional",
|
155
|
+
"ST" => "required",
|
156
|
+
"C" => "required",
|
157
|
+
"OU" => "optional"
|
158
|
+
)
|
159
|
+
R509::Config::CaProfile.new(
|
160
|
+
:basic_constraints => "CA:FALSE",
|
161
|
+
:key_usage => ["digitalSignature","keyEncipherment"],
|
162
|
+
:extended_key_usage => ["serverAuth"],
|
163
|
+
:certificate_policies => [
|
164
|
+
[
|
165
|
+
"policyIdentifier=2.16.840.1.12345.1.2.3.4.1",
|
166
|
+
"CPS.1=http://example.com/cps"
|
167
|
+
]
|
168
|
+
],
|
169
|
+
:subject_item_policy => subject_item_policy
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.test_ca_subroot_profile
|
174
|
+
R509::Config::CaProfile.new(
|
175
|
+
:basic_constraints => "CA:TRUE,pathlen:0",
|
176
|
+
:key_usage => ["keyCertSign","cRLSign"],
|
177
|
+
:extended_key_usage => [],
|
178
|
+
:certificate_policies => nil)
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.test_ca_ocspsigner_profile
|
182
|
+
R509::Config::CaProfile.new(
|
183
|
+
:basic_constraints => "CA:FALSE",
|
184
|
+
:key_usage => ["digitalSignature"],
|
185
|
+
:extended_key_usage => ["OCSPSigning"],
|
186
|
+
:certificate_policies => nil)
|
187
|
+
end
|
188
|
+
|
189
|
+
# @return [R509::Config::CaConfig]
|
190
|
+
def self.test_ca_config
|
191
|
+
crl_list_sio = StringIO.new
|
192
|
+
crl_list_sio.set_encoding("BINARY") if crl_list_sio.respond_to?(:set_encoding)
|
193
|
+
crl_number_sio = StringIO.new
|
194
|
+
crl_number_sio.set_encoding("BINARY") if crl_number_sio.respond_to?(:set_encoding)
|
195
|
+
|
196
|
+
opts = {
|
197
|
+
:ca_cert => test_ca_cert(),
|
198
|
+
:cdp_location => 'URI:http://crl.domain.com/test_ca.crl',
|
199
|
+
:ocsp_location => 'URI:http://ocsp.domain.com',
|
200
|
+
:ocsp_start_skew_seconds => 3600,
|
201
|
+
:ocsp_validity_hours => 48,
|
202
|
+
:crl_list_file => crl_list_sio,
|
203
|
+
:crl_number_file => crl_number_sio
|
204
|
+
}
|
205
|
+
ret = R509::Config::CaConfig.new(opts)
|
206
|
+
|
207
|
+
ret.set_profile("server", self.test_ca_server_profile)
|
208
|
+
ret.set_profile("subroot", self.test_ca_subroot_profile)
|
209
|
+
ret.set_profile("ocspsigner", self.test_ca_ocspsigner_profile)
|
210
|
+
ret.set_profile("server_with_subject_item_policy", self.test_ca_server_profile_with_subject_item_policy)
|
211
|
+
|
212
|
+
ret
|
213
|
+
end
|
214
|
+
|
215
|
+
# @return [R509::Config::CaConfig]
|
216
|
+
def self.test_ca_no_profile_config
|
217
|
+
crl_list_sio = StringIO.new
|
218
|
+
crl_list_sio.set_encoding("BINARY") if crl_list_sio.respond_to?(:set_encoding)
|
219
|
+
crl_number_sio = StringIO.new
|
220
|
+
crl_number_sio.set_encoding("BINARY") if crl_number_sio.respond_to?(:set_encoding)
|
221
|
+
|
222
|
+
opts = {
|
223
|
+
:ca_cert => test_ca_cert(),
|
224
|
+
:cdp_location => 'URI:http://crl.domain.com/test_ca.crl',
|
225
|
+
:ocsp_location => 'URI:http://ocsp.domain.com',
|
226
|
+
:ocsp_start_skew_seconds => 3600,
|
227
|
+
:ocsp_validity_hours => 48,
|
228
|
+
:crl_list_file => crl_list_sio,
|
229
|
+
:crl_number_file => crl_number_sio
|
230
|
+
}
|
231
|
+
R509::Config::CaConfig.new(opts)
|
232
|
+
end
|
233
|
+
end
|