r509 0.8
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 +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
|