httpclient 2.6.0.1 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/httpclient +5 -1
- data/lib/http-access2.rb +1 -1
- data/lib/httpclient.rb +37 -5
- data/lib/httpclient/auth.rb +3 -3
- data/lib/httpclient/cacert.pem +3952 -0
- data/lib/httpclient/{cacert.p7s → cacert1024.pem} +0 -0
- data/lib/httpclient/connection.rb +1 -1
- data/lib/httpclient/cookie.rb +12 -4
- data/lib/httpclient/http.rb +1 -1
- data/lib/httpclient/jruby_ssl_socket.rb +526 -0
- data/lib/httpclient/session.rb +161 -244
- data/lib/httpclient/ssl_config.rb +54 -17
- data/lib/httpclient/ssl_socket.rb +149 -0
- data/lib/httpclient/timeout.rb +1 -1
- data/lib/httpclient/util.rb +23 -1
- data/lib/httpclient/version.rb +1 -1
- data/lib/oauthclient.rb +1 -1
- data/test/{ca-chain.cert → ca-chain.pem} +0 -0
- data/test/helper.rb +7 -5
- data/test/test_auth.rb +27 -8
- data/test/test_cookie.rb +2 -2
- data/test/test_httpclient.rb +57 -21
- data/test/test_ssl.rb +70 -21
- data/test/test_webagent-cookie.rb +2 -2
- metadata +13 -10
File without changes
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# HTTPClient - HTTP client library.
|
2
|
-
# Copyright (C) 2000-
|
2
|
+
# Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
3
3
|
#
|
4
4
|
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
5
5
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
data/lib/httpclient/cookie.rb
CHANGED
@@ -6,13 +6,13 @@ require 'http-cookie'
|
|
6
6
|
|
7
7
|
class HTTPClient
|
8
8
|
class CookieManager
|
9
|
-
attr_reader :format
|
9
|
+
attr_reader :format, :jar
|
10
10
|
attr_accessor :cookies_file
|
11
11
|
|
12
|
-
def initialize(cookies_file = nil, format = WebAgentSaver)
|
12
|
+
def initialize(cookies_file = nil, format = WebAgentSaver, jar = HTTP::CookieJar.new)
|
13
13
|
@cookies_file = cookies_file
|
14
14
|
@format = format
|
15
|
-
@jar =
|
15
|
+
@jar = jar
|
16
16
|
load_cookies if @cookies_file
|
17
17
|
end
|
18
18
|
|
@@ -174,6 +174,7 @@ class WebAgent
|
|
174
174
|
CookieManager = ::HTTPClient::CookieManager
|
175
175
|
|
176
176
|
class Cookie < HTTP::Cookie
|
177
|
+
@@domain_warned = false
|
177
178
|
@@warned = false
|
178
179
|
|
179
180
|
def url
|
@@ -194,7 +195,7 @@ class WebAgent
|
|
194
195
|
alias original_domain domain
|
195
196
|
|
196
197
|
def domain
|
197
|
-
|
198
|
+
domain_warning
|
198
199
|
self.original_domain
|
199
200
|
end
|
200
201
|
|
@@ -205,6 +206,13 @@ class WebAgent
|
|
205
206
|
|
206
207
|
private
|
207
208
|
|
209
|
+
def domain_warning
|
210
|
+
unless @@domain_warned
|
211
|
+
warn('Cookie#domain returns dot-less domain name now. Use Cookie#dot_domain if you need "." at the beginning.')
|
212
|
+
@@domain_warned = true
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
208
216
|
def deprecated(old, new)
|
209
217
|
unless @@warned
|
210
218
|
warn("WebAgent::Cookie is deprecated and will be replaced with HTTP::Cookie in the near future. Please use Cookie##{new} instead of Cookie##{old} for the replacement.")
|
data/lib/httpclient/http.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
# HTTPClient - HTTP client library.
|
4
|
-
# Copyright (C) 2000-
|
4
|
+
# Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
5
5
|
#
|
6
6
|
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
7
7
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
@@ -0,0 +1,526 @@
|
|
1
|
+
# HTTPClient - HTTP client library.
|
2
|
+
# Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
3
|
+
#
|
4
|
+
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
5
|
+
# redistribute it and/or modify it under the same terms of Ruby's license;
|
6
|
+
# either the dual license version in 2003, or any later version.
|
7
|
+
|
8
|
+
|
9
|
+
require 'java'
|
10
|
+
require 'httpclient/ssl_config'
|
11
|
+
|
12
|
+
|
13
|
+
class HTTPClient
|
14
|
+
|
15
|
+
unless defined?(SSLSocket)
|
16
|
+
|
17
|
+
class JavaSocketWrap
|
18
|
+
java_import 'java.io.BufferedInputStream'
|
19
|
+
BUF_SIZE = 1024 * 16
|
20
|
+
|
21
|
+
def initialize(socket, debug_dev = nil)
|
22
|
+
@socket = socket
|
23
|
+
@debug_dev = debug_dev
|
24
|
+
@outstr = @socket.getOutputStream
|
25
|
+
@instr = BufferedInputStream.new(@socket.getInputStream)
|
26
|
+
@buf = (' ' * BUF_SIZE).to_java_bytes
|
27
|
+
@bufstr = ''
|
28
|
+
end
|
29
|
+
|
30
|
+
def close
|
31
|
+
@socket.close
|
32
|
+
end
|
33
|
+
|
34
|
+
def closed?
|
35
|
+
@socket.isClosed
|
36
|
+
end
|
37
|
+
|
38
|
+
def eof?
|
39
|
+
@socket.isClosed
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def gets(rs)
|
44
|
+
while (size = @bufstr.index(rs)).nil?
|
45
|
+
if fill() == -1
|
46
|
+
size = @bufstr.size
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
str = @bufstr.slice!(0, size + rs.size)
|
51
|
+
debug(str)
|
52
|
+
str
|
53
|
+
end
|
54
|
+
|
55
|
+
def read(size, buf = nil)
|
56
|
+
while @bufstr.size < size
|
57
|
+
if fill() == -1
|
58
|
+
break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
str = @bufstr.slice!(0, size)
|
62
|
+
debug(str)
|
63
|
+
if buf
|
64
|
+
buf.replace(str)
|
65
|
+
else
|
66
|
+
str
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def readpartial(size, buf = nil)
|
71
|
+
while @bufstr.size == 0
|
72
|
+
if fill() == -1
|
73
|
+
raise EOFError.new('end of file reached')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
str = @bufstr.slice!(0, size)
|
77
|
+
debug(str)
|
78
|
+
if buf
|
79
|
+
buf.replace(str)
|
80
|
+
else
|
81
|
+
str
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def <<(str)
|
86
|
+
rv = @outstr.write(str.to_java_bytes)
|
87
|
+
debug(str)
|
88
|
+
rv
|
89
|
+
end
|
90
|
+
|
91
|
+
def flush
|
92
|
+
@socket.flush
|
93
|
+
end
|
94
|
+
|
95
|
+
def sync
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def sync=(sync)
|
100
|
+
unless sync
|
101
|
+
raise "sync = false is not supported. This option was introduced for backward compatibility just in case."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def fill
|
108
|
+
size = @instr.read(@buf)
|
109
|
+
if size > 0
|
110
|
+
@bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
|
111
|
+
end
|
112
|
+
size
|
113
|
+
end
|
114
|
+
|
115
|
+
def debug(str)
|
116
|
+
@debug_dev << str if @debug_dev && str
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class JRubySSLSocket < JavaSocketWrap
|
121
|
+
java_import 'java.io.ByteArrayInputStream'
|
122
|
+
java_import 'java.io.InputStreamReader'
|
123
|
+
java_import 'java.net.Socket'
|
124
|
+
java_import 'java.security.KeyStore'
|
125
|
+
java_import 'java.security.cert.Certificate'
|
126
|
+
java_import 'java.security.cert.CertificateFactory'
|
127
|
+
java_import 'javax.net.ssl.KeyManagerFactory'
|
128
|
+
java_import 'javax.net.ssl.SSLContext'
|
129
|
+
java_import 'javax.net.ssl.SSLSocketFactory'
|
130
|
+
java_import 'javax.net.ssl.TrustManager'
|
131
|
+
java_import 'javax.net.ssl.TrustManagerFactory'
|
132
|
+
java_import 'javax.net.ssl.X509TrustManager'
|
133
|
+
java_import 'org.jruby.ext.openssl.x509store.PEMInputOutput'
|
134
|
+
|
135
|
+
class JavaCertificate
|
136
|
+
attr_reader :cert
|
137
|
+
|
138
|
+
def initialize(cert)
|
139
|
+
@cert = cert
|
140
|
+
end
|
141
|
+
|
142
|
+
def subject
|
143
|
+
@cert.getSubjectDN
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_text
|
147
|
+
@cert.toString
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_pem
|
151
|
+
'(not in PEM format)'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class SSLStoreContext
|
156
|
+
attr_reader :current_cert, :chain, :error_depth, :error, :error_string
|
157
|
+
|
158
|
+
def initialize(current_cert, chain, error_depth, error, error_string)
|
159
|
+
@current_cert, @chain, @error_depth, @error, @error_string =
|
160
|
+
current_cert, chain, error_depth, error, error_string
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class JSSEVerifyCallback
|
165
|
+
def initialize(verify_callback)
|
166
|
+
@verify_callback = verify_callback
|
167
|
+
end
|
168
|
+
|
169
|
+
def call(is_ok, chain, error_depth = -1, error = -1, error_string = '(unknown)')
|
170
|
+
if @verify_callback
|
171
|
+
ruby_chain = chain.map { |cert|
|
172
|
+
JavaCertificate.new(cert)
|
173
|
+
}.reverse
|
174
|
+
# NOTE: The order depends on provider implementation
|
175
|
+
ruby_chain.each do |cert|
|
176
|
+
is_ok = @verify_callback.call(
|
177
|
+
is_ok,
|
178
|
+
SSLStoreContext.new(cert, ruby_chain, error_depth, error, error_string)
|
179
|
+
)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
is_ok
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class VerifyNoneTrustManagerFactory
|
187
|
+
class VerifyNoneTrustManager
|
188
|
+
include X509TrustManager
|
189
|
+
|
190
|
+
def initialize(verify_callback)
|
191
|
+
@verify_callback = JSSEVerifyCallback.new(verify_callback)
|
192
|
+
end
|
193
|
+
|
194
|
+
def checkServerTrusted(chain, authType)
|
195
|
+
@verify_callback.call(true, chain)
|
196
|
+
end
|
197
|
+
|
198
|
+
def checkClientTrusted(chain, authType); end
|
199
|
+
def getAcceptedIssuers; end
|
200
|
+
end
|
201
|
+
|
202
|
+
def initialize(verify_callback = nil)
|
203
|
+
@verify_callback = verify_callback
|
204
|
+
end
|
205
|
+
|
206
|
+
def init(trustStore)
|
207
|
+
@managers = [VerifyNoneTrustManager.new(@verify_callback)].to_java(X509TrustManager)
|
208
|
+
end
|
209
|
+
|
210
|
+
def getTrustManagers
|
211
|
+
@managers
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class SystemTrustManagerFactory
|
216
|
+
class SystemTrustManager
|
217
|
+
include X509TrustManager
|
218
|
+
|
219
|
+
def initialize(original, verify_callback)
|
220
|
+
@original = original
|
221
|
+
@verify_callback = JSSEVerifyCallback.new(verify_callback)
|
222
|
+
end
|
223
|
+
|
224
|
+
def checkServerTrusted(chain, authType)
|
225
|
+
is_ok = false
|
226
|
+
excn = nil
|
227
|
+
# TODO can we detect the depth from excn?
|
228
|
+
error_depth = -1
|
229
|
+
error = nil
|
230
|
+
error_message = nil
|
231
|
+
begin
|
232
|
+
@original.checkServerTrusted(chain, authType)
|
233
|
+
is_ok = true
|
234
|
+
rescue java.security.cert.CertificateException => excn
|
235
|
+
is_ok = false
|
236
|
+
error = excn.class.name
|
237
|
+
error_message = excn.getMessage
|
238
|
+
end
|
239
|
+
is_ok = @verify_callback.call(is_ok, chain, error_depth, error, error_message)
|
240
|
+
unless is_ok
|
241
|
+
excn ||= OpenSSL::SSL::SSLError.new('verifycallback failed')
|
242
|
+
raise excn
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def checkClientTrusted(chain, authType); end
|
247
|
+
def getAcceptedIssuers; end
|
248
|
+
end
|
249
|
+
|
250
|
+
def initialize(verify_callback = nil)
|
251
|
+
@verify_callback = verify_callback
|
252
|
+
end
|
253
|
+
|
254
|
+
def init(trust_store)
|
255
|
+
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
|
256
|
+
tmf.java_method(:init, [KeyStore]).call(trust_store)
|
257
|
+
@original = tmf.getTrustManagers.find { |tm|
|
258
|
+
tm.is_a?(X509TrustManager)
|
259
|
+
}
|
260
|
+
@managers = [SystemTrustManager.new(@original, @verify_callback)].to_java(X509TrustManager)
|
261
|
+
end
|
262
|
+
|
263
|
+
def getTrustManagers
|
264
|
+
@managers
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
module PEMUtils
|
269
|
+
def self.read_certificate(pem)
|
270
|
+
pem = pem.sub(/-----BEGIN CERTIFICATE-----/, '').sub(/-----END CERTIFICATE-----/, '')
|
271
|
+
der = pem.unpack('m*').first
|
272
|
+
cf = CertificateFactory.getInstance('X.509')
|
273
|
+
cf.generateCertificate(ByteArrayInputStream.new(der.to_java_bytes))
|
274
|
+
end
|
275
|
+
|
276
|
+
def self.read_private_key(pem, password)
|
277
|
+
if password
|
278
|
+
password = password.unpack('C*').to_java(:char)
|
279
|
+
end
|
280
|
+
PEMInputOutput.read_private_key(InputStreamReader.new(ByteArrayInputStream.new(pem.to_java_bytes)), password)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class KeyStoreLoader
|
285
|
+
PASSWORD = 16.times.map { rand(256) }.to_java(:char)
|
286
|
+
|
287
|
+
def initialize
|
288
|
+
@keystore = KeyStore.getInstance('JKS')
|
289
|
+
@keystore.load(nil)
|
290
|
+
end
|
291
|
+
|
292
|
+
def add(cert_file, key_file, password)
|
293
|
+
cert_str = cert_file.respond_to?(:to_pem) ? cert_file.to_pem : File.read(cert_file.to_s)
|
294
|
+
cert = PEMUtils.read_certificate(cert_str)
|
295
|
+
@keystore.setCertificateEntry('client_cert', cert)
|
296
|
+
key_str = key_file.respond_to?(:to_pem) ? key_file.to_pem : File.read(key_file.to_s)
|
297
|
+
key_pair = PEMUtils.read_private_key(key_str, password)
|
298
|
+
@keystore.setKeyEntry('client_key', key_pair.getPrivate, PASSWORD, [cert].to_java(Certificate))
|
299
|
+
end
|
300
|
+
|
301
|
+
def keystore
|
302
|
+
@keystore
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class TrustStoreLoader
|
307
|
+
attr_reader :size
|
308
|
+
|
309
|
+
def initialize
|
310
|
+
@trust_store = KeyStore.getInstance('JKS')
|
311
|
+
@trust_store.load(nil)
|
312
|
+
@size = 0
|
313
|
+
end
|
314
|
+
|
315
|
+
def add(file_or_dir)
|
316
|
+
return if file_or_dir == :default
|
317
|
+
if File.directory?(file_or_dir)
|
318
|
+
warn('directory not yet supported')
|
319
|
+
else
|
320
|
+
pem = nil
|
321
|
+
File.read(file_or_dir).each_line do |line|
|
322
|
+
case line
|
323
|
+
when /-----BEGIN CERTIFICATE-----/
|
324
|
+
pem = ''
|
325
|
+
when /-----END CERTIFICATE-----/
|
326
|
+
cert = PEMUtils.read_certificate(pem)
|
327
|
+
@size += 1
|
328
|
+
@trust_store.setCertificateEntry("cert_#{@size}", cert)
|
329
|
+
else
|
330
|
+
if pem
|
331
|
+
pem << line
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def trust_store
|
339
|
+
if @size == 0
|
340
|
+
nil
|
341
|
+
else
|
342
|
+
@trust_store
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Ported from commons-httpclient 'BrowserCompatHostnameVerifier'
|
348
|
+
class BrowserCompatHostnameVerifier
|
349
|
+
BAD_COUNTRY_2LDS = %w(ac co com ed edu go gouv gov info lg ne net or org).sort
|
350
|
+
require 'ipaddr'
|
351
|
+
|
352
|
+
def extract_sans(cert, subject_type)
|
353
|
+
sans = cert.getSubjectAlternativeNames rescue nil
|
354
|
+
if sans.nil?
|
355
|
+
return nil
|
356
|
+
end
|
357
|
+
sans.find_all { |san|
|
358
|
+
san.first.to_i == subject_type
|
359
|
+
}.map { |san|
|
360
|
+
san[1]
|
361
|
+
}
|
362
|
+
end
|
363
|
+
|
364
|
+
def extract_cn(cert)
|
365
|
+
subject = cert.getSubjectX500Principal()
|
366
|
+
if subject
|
367
|
+
subject_dn = javax.naming.ldap.LdapName.new(subject.toString)
|
368
|
+
subject_dn.getRdns.to_a.reverse.each do |rdn|
|
369
|
+
attributes = rdn.toAttributes
|
370
|
+
cn = attributes.get('cn')
|
371
|
+
if cn
|
372
|
+
if value = cn.get
|
373
|
+
return value.to_s
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def ipaddr?(addr)
|
381
|
+
!(IPAddr.new(addr) rescue nil).nil?
|
382
|
+
end
|
383
|
+
|
384
|
+
def verify(hostname, cert)
|
385
|
+
is_ipaddr = ipaddr?(hostname)
|
386
|
+
sans = extract_sans(cert, is_ipaddr ? 7 : 2)
|
387
|
+
cn = extract_cn(cert)
|
388
|
+
if sans
|
389
|
+
sans.each do |san|
|
390
|
+
return true if match_identify(hostname, san)
|
391
|
+
end
|
392
|
+
raise OpenSSL::SSL::SSLError.new("Certificate for <#{hostname}> doesn't match any of the subject alternative names: #{sans}")
|
393
|
+
elsif cn
|
394
|
+
return true if match_identify(hostname, cn)
|
395
|
+
raise OpenSSL::SSL::SSLError.new("Certificate for <#{hostname}> doesn't match common name of the certificate subject: #{cn}")
|
396
|
+
end
|
397
|
+
raise OpenSSL::SSL::SSLError.new("Certificate subject for for <#{hostname}> doesn't contain a common name and does not have alternative names")
|
398
|
+
end
|
399
|
+
|
400
|
+
def match_identify(hostname, identity)
|
401
|
+
if hostname.nil?
|
402
|
+
return false
|
403
|
+
end
|
404
|
+
hostname = hostname.downcase
|
405
|
+
identity = identity.downcase
|
406
|
+
parts = identity.split('.')
|
407
|
+
if parts.length >= 3 && parts.first.end_with?('*') && valid_country_wildcard(parts)
|
408
|
+
create_wildcard_regexp(identity) =~ hostname
|
409
|
+
else
|
410
|
+
hostname == identity
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def create_wildcard_regexp(value)
|
415
|
+
# Escape first then search '\*' for meta-char interpolation
|
416
|
+
labels = value.split('.').map { |e| Regexp.escape(e) }
|
417
|
+
# Handle '*'s only at the left-most label, exclude A-label and U-label
|
418
|
+
labels[0].gsub!(/\\\*/, '[^.]+') if !labels[0].start_with?('xn\-\-') and labels[0].ascii_only?
|
419
|
+
/\A#{labels.join('\.')}\z/i
|
420
|
+
end
|
421
|
+
|
422
|
+
def valid_country_wildcard(parts)
|
423
|
+
if parts.length != 3 || parts[2].length != 2
|
424
|
+
true
|
425
|
+
else
|
426
|
+
!BAD_COUNTRY_2LDS.include?(parts[1])
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
def self.create_socket(session)
|
432
|
+
site = session.proxy || session.dest
|
433
|
+
socket = Socket.new(site.host, site.port)
|
434
|
+
begin
|
435
|
+
if session.proxy
|
436
|
+
session.connect_ssl_proxy(JavaSocketWrap.new(socket), Util.urify(session.dest.to_s))
|
437
|
+
end
|
438
|
+
rescue
|
439
|
+
socket.close
|
440
|
+
raise
|
441
|
+
end
|
442
|
+
new(socket, session.dest, session.ssl_config, session.debug_dev)
|
443
|
+
end
|
444
|
+
|
445
|
+
def initialize(socket, dest, config, debug_dev = nil)
|
446
|
+
if config.ssl_version == :auto
|
447
|
+
ssl_version = 'TLSv1'
|
448
|
+
else
|
449
|
+
ssl_version = config.to_s.gsub(/_/, '.')
|
450
|
+
end
|
451
|
+
unless config.cert_store_crl_items.empty?
|
452
|
+
raise NotImplementedError.new('Manual CRL configuration is not yet supported')
|
453
|
+
end
|
454
|
+
|
455
|
+
km = nil
|
456
|
+
if config.client_cert && config.client_key
|
457
|
+
loader = KeyStoreLoader.new
|
458
|
+
loader.add(config.client_cert, config.client_key, config.client_key_pass)
|
459
|
+
kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
|
460
|
+
kmf.init(loader.keystore, KeyStoreLoader::PASSWORD)
|
461
|
+
km = kmf.getKeyManagers
|
462
|
+
end
|
463
|
+
|
464
|
+
trust_store = nil
|
465
|
+
verify_callback = config.verify_callback || config.method(:default_verify_callback)
|
466
|
+
if config.verify_mode == nil
|
467
|
+
tmf = VerifyNoneTrustManagerFactory.new(verify_callback)
|
468
|
+
else
|
469
|
+
tmf = SystemTrustManagerFactory.new(verify_callback)
|
470
|
+
loader = TrustStoreLoader.new
|
471
|
+
config.cert_store_items.each do |item|
|
472
|
+
loader.add(item)
|
473
|
+
end
|
474
|
+
trust_store = loader.trust_store
|
475
|
+
end
|
476
|
+
tmf.init(trust_store)
|
477
|
+
tm = tmf.getTrustManagers
|
478
|
+
|
479
|
+
ctx = SSLContext.getInstance(ssl_version)
|
480
|
+
ctx.init(km, tm, nil)
|
481
|
+
if config.timeout
|
482
|
+
ctx.getClientSessionContext.setSessionTimeout(config.timeout)
|
483
|
+
end
|
484
|
+
|
485
|
+
factory = ctx.getSocketFactory
|
486
|
+
begin
|
487
|
+
ssl_socket = factory.createSocket(socket, dest.host, dest.port, true)
|
488
|
+
ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String))
|
489
|
+
if config.ciphers != SSLConfig::CIPHERS_DEFAULT
|
490
|
+
ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
|
491
|
+
end
|
492
|
+
ssl_socket.startHandshake
|
493
|
+
@peer_cert = JavaCertificate.new(ssl_socket.getSession.getPeerCertificates.first)
|
494
|
+
@ciphersuite = ssl_socket.getSession.getCipherSuite
|
495
|
+
post_connection_check(dest.host, @peer_cert)
|
496
|
+
rescue java.security.GeneralSecurityException => e
|
497
|
+
raise OpenSSL::SSL::SSLError.new(e.getMessage)
|
498
|
+
rescue javax.net.ssl.SSLException => e
|
499
|
+
raise OpenSSL::SSL::SSLError.new(e.getMessage)
|
500
|
+
rescue java.net.SocketException => e
|
501
|
+
raise OpenSSL::SSL::SSLError.new(e.getMessage)
|
502
|
+
end
|
503
|
+
|
504
|
+
super(ssl_socket, debug_dev)
|
505
|
+
end
|
506
|
+
|
507
|
+
def peer_cert
|
508
|
+
@peer_cert
|
509
|
+
end
|
510
|
+
|
511
|
+
def ciphersuite
|
512
|
+
@ciphersuite
|
513
|
+
end
|
514
|
+
|
515
|
+
private
|
516
|
+
|
517
|
+
def post_connection_check(hostname, wrap_cert)
|
518
|
+
BrowserCompatHostnameVerifier.new.verify(hostname, wrap_cert.cert)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
SSLSocket = JRubySSLSocket
|
523
|
+
|
524
|
+
end
|
525
|
+
|
526
|
+
end
|