httpclient 2.6.0.1 → 2.7.0
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.
- 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
|