1require 'rubygems/test_case'
2require 'rubygems/security'
3require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
4
5class TestGemSecurity < Gem::TestCase
6
7  CHILD_KEY = load_key 'child'
8
9  ALTERNATE_CERT = load_cert 'child'
10  CHILD_CERT     = load_cert 'child'
11  EXPIRED_CERT   = load_cert 'expired'
12
13  def setup
14    super
15
16    @SEC = Gem::Security
17  end
18
19  def test_class_create_cert
20    name = PUBLIC_CERT.subject
21    key = PRIVATE_KEY
22
23    cert = @SEC.create_cert name, key, 60, Gem::Security::EXTENSIONS, 5
24
25    assert_kind_of OpenSSL::X509::Certificate, cert
26
27    assert_equal    2,                     cert.version
28    assert_equal    5,                     cert.serial
29    assert_equal    key.public_key.to_pem, cert.public_key.to_pem
30    assert_in_delta Time.now,              cert.not_before, 10
31    assert_in_delta Time.now + 60,         cert.not_after, 10
32    assert_equal    name.to_s,             cert.subject.to_s
33
34    assert_equal 3, cert.extensions.length,
35                 cert.extensions.map { |e| e.to_a.first }
36
37    constraints = cert.extensions.find { |ext| ext.oid == 'basicConstraints' }
38    assert_equal 'CA:FALSE', constraints.value
39
40    key_usage = cert.extensions.find { |ext| ext.oid == 'keyUsage' }
41    assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
42                 key_usage.value
43
44    key_ident = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
45    assert_equal 59, key_ident.value.length
46    assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
47                 key_ident.value
48
49    assert_equal '', cert.issuer.to_s
50    assert_equal name.to_s, cert.subject.to_s
51  end
52
53  def test_class_create_cert_self_signed
54    subject = PUBLIC_CERT.subject
55
56    cert = @SEC.create_cert_self_signed subject, PRIVATE_KEY, 60
57
58    assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
59  end
60
61  def test_class_create_cert_email
62    email = 'nobody@example'
63    name = PUBLIC_CERT.subject
64    key = PRIVATE_KEY
65
66    cert = @SEC.create_cert_email email, key, 60
67
68    assert_kind_of OpenSSL::X509::Certificate, cert
69
70    assert_equal    2,                     cert.version
71    assert_equal    1,                     cert.serial
72    assert_equal    key.public_key.to_pem, cert.public_key.to_pem
73    assert_in_delta Time.now,              cert.not_before, 10
74    assert_in_delta Time.now + 60,         cert.not_after, 10
75    assert_equal    name.to_s,             cert.subject.to_s
76    assert_equal    name.to_s,             cert.issuer.to_s
77
78    assert_equal 5, cert.extensions.length,
79                 cert.extensions.map { |e| e.to_a.first }
80
81    constraints = cert.extensions.find { |ext| ext.oid == 'subjectAltName' }
82    assert_equal 'email:nobody@example', constraints.value
83
84    constraints = cert.extensions.find { |ext| ext.oid == 'basicConstraints' }
85    assert_equal 'CA:FALSE', constraints.value
86
87    key_usage = cert.extensions.find { |ext| ext.oid == 'keyUsage' }
88    assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
89                 key_usage.value
90
91    key_ident = cert.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
92    assert_equal 59, key_ident.value.length
93    assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
94                 key_ident.value
95  end
96
97  def test_class_create_key
98    key = @SEC.create_key 256
99
100    assert_kind_of OpenSSL::PKey::RSA, key
101  end
102
103  def test_class_email_to_name
104    assert_equal '/CN=nobody/DC=example',
105                 @SEC.email_to_name('nobody@example').to_s
106
107    assert_equal '/CN=nobody/DC=example/DC=com',
108                 @SEC.email_to_name('nobody@example.com').to_s
109
110    assert_equal '/CN=no.body/DC=example',
111                 @SEC.email_to_name('no.body@example').to_s
112
113    assert_equal '/CN=no_body/DC=example',
114                 @SEC.email_to_name('no+body@example').to_s
115  end
116
117  def test_class_re_sign
118    re_signed = Gem::Security.re_sign EXPIRED_CERT, PRIVATE_KEY, 60
119
120    assert_in_delta Time.now,      re_signed.not_before, 10
121    assert_in_delta Time.now + 60, re_signed.not_after,  10
122    assert_equal EXPIRED_CERT.serial + 1, re_signed.serial
123
124    assert re_signed.verify PUBLIC_KEY
125  end
126
127  def test_class_re_sign_not_self_signed
128    e = assert_raises Gem::Security::Exception do
129      Gem::Security.re_sign CHILD_CERT, CHILD_KEY
130    end
131
132    child_alt_name = CHILD_CERT.extensions.find do |extension|
133      extension.oid == 'subjectAltName'
134    end
135
136    assert_equal "#{child_alt_name.value} is not self-signed, contact " +
137                 "#{ALTERNATE_CERT.issuer} to obtain a valid certificate",
138                 e.message
139  end
140
141  def test_class_re_sign_wrong_key
142    e = assert_raises Gem::Security::Exception do
143      Gem::Security.re_sign ALTERNATE_CERT, PRIVATE_KEY
144    end
145
146    assert_equal "incorrect signing key for re-signing " +
147                 "#{ALTERNATE_CERT.subject}",
148                 e.message
149  end
150
151  def test_class_reset
152    trust_dir = @SEC.trust_dir
153
154    @SEC.reset
155
156    refute_equal trust_dir, @SEC.trust_dir
157  end
158
159  def test_class_sign
160    issuer = PUBLIC_CERT.subject
161    signee = OpenSSL::X509::Name.parse "/CN=signee/DC=example"
162
163    key  = PRIVATE_KEY
164    cert = OpenSSL::X509::Certificate.new
165    cert.subject = signee
166
167    cert.subject    = signee
168    cert.public_key = key.public_key
169
170    signed = @SEC.sign cert, key, PUBLIC_CERT, 60
171
172    assert_equal    key.public_key.to_pem, signed.public_key.to_pem
173    assert_equal    signee.to_s,           signed.subject.to_s
174    assert_equal    issuer.to_s,           signed.issuer.to_s
175
176    assert_in_delta Time.now,              signed.not_before, 10
177    assert_in_delta Time.now + 60,         signed.not_after, 10
178
179    assert_equal 4, signed.extensions.length,
180                 signed.extensions.map { |e| e.to_a.first }
181
182    constraints = signed.extensions.find { |ext| ext.oid == 'issuerAltName' }
183    assert_equal 'email:nobody@example', constraints.value, 'issuerAltName'
184
185    constraints = signed.extensions.find { |ext| ext.oid == 'basicConstraints' }
186    assert_equal 'CA:FALSE', constraints.value
187
188    key_usage = signed.extensions.find { |ext| ext.oid == 'keyUsage' }
189    assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
190                 key_usage.value
191
192    key_ident =
193      signed.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
194    assert_equal 59, key_ident.value.length
195    assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
196                 key_ident.value
197
198    assert signed.verify key
199  end
200
201  def test_class_sign_AltName
202    issuer = PUBLIC_CERT.subject
203    signee = OpenSSL::X509::Name.parse "/CN=signee/DC=example"
204
205    cert = @SEC.create_cert_email 'signee@example', PRIVATE_KEY
206
207    signed = @SEC.sign cert, PRIVATE_KEY, PUBLIC_CERT, 60
208
209    assert_equal    PUBLIC_KEY.to_pem, signed.public_key.to_pem
210    assert_equal    signee.to_s,       signed.subject.to_s
211    assert_equal    issuer.to_s,       signed.issuer.to_s
212
213    assert_in_delta Time.now,          signed.not_before, 10
214    assert_in_delta Time.now + 60,     signed.not_after, 10
215
216    assert_equal 5, signed.extensions.length,
217                 signed.extensions.map { |e| e.to_a.first }
218
219    constraints = signed.extensions.find { |ext| ext.oid == 'issuerAltName' }
220    assert_equal 'email:nobody@example', constraints.value, 'issuerAltName'
221
222    constraints = signed.extensions.find { |ext| ext.oid == 'subjectAltName' }
223    assert_equal 'email:signee@example', constraints.value, 'subjectAltName'
224
225    constraints = signed.extensions.find { |ext| ext.oid == 'basicConstraints' }
226    assert_equal 'CA:FALSE', constraints.value
227
228    key_usage = signed.extensions.find { |ext| ext.oid == 'keyUsage' }
229    assert_equal 'Digital Signature, Key Encipherment, Data Encipherment',
230                 key_usage.value
231
232    key_ident =
233      signed.extensions.find { |ext| ext.oid == 'subjectKeyIdentifier' }
234    assert_equal 59, key_ident.value.length
235    assert_equal '5F:43:6E:F6:9A:8E:45:25:E9:22:E3:7D:37:5E:A4:D5:36:02:85:1B',
236                 key_ident.value
237
238    assert signed.verify PUBLIC_KEY
239  end
240
241  def test_class_trust_dir
242    trust_dir = @SEC.trust_dir
243
244    expected = File.join Gem.user_home, '.gem/trust'
245
246    assert_equal expected, trust_dir.dir
247  end
248
249end
250
251