httpclient-fixcerts 2.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +98 -0
  3. data/bin/httpclient +77 -0
  4. data/bin/jsonclient +85 -0
  5. data/lib/hexdump.rb +50 -0
  6. data/lib/http-access2/cookie.rb +1 -0
  7. data/lib/http-access2/http.rb +1 -0
  8. data/lib/http-access2.rb +55 -0
  9. data/lib/httpclient/auth.rb +924 -0
  10. data/lib/httpclient/cacert.pem +3952 -0
  11. data/lib/httpclient/cacert1024.pem +3866 -0
  12. data/lib/httpclient/connection.rb +88 -0
  13. data/lib/httpclient/cookie.rb +220 -0
  14. data/lib/httpclient/http.rb +1082 -0
  15. data/lib/httpclient/include_client.rb +85 -0
  16. data/lib/httpclient/jruby_ssl_socket.rb +594 -0
  17. data/lib/httpclient/session.rb +960 -0
  18. data/lib/httpclient/ssl_config.rb +433 -0
  19. data/lib/httpclient/ssl_socket.rb +150 -0
  20. data/lib/httpclient/timeout.rb +140 -0
  21. data/lib/httpclient/util.rb +222 -0
  22. data/lib/httpclient/version.rb +3 -0
  23. data/lib/httpclient/webagent-cookie.rb +459 -0
  24. data/lib/httpclient.rb +1332 -0
  25. data/lib/jsonclient.rb +66 -0
  26. data/lib/oauthclient.rb +111 -0
  27. data/sample/async.rb +8 -0
  28. data/sample/auth.rb +11 -0
  29. data/sample/cookie.rb +18 -0
  30. data/sample/dav.rb +103 -0
  31. data/sample/howto.rb +49 -0
  32. data/sample/jsonclient.rb +67 -0
  33. data/sample/oauth_buzz.rb +57 -0
  34. data/sample/oauth_friendfeed.rb +59 -0
  35. data/sample/oauth_twitter.rb +61 -0
  36. data/sample/ssl/0cert.pem +22 -0
  37. data/sample/ssl/0key.pem +30 -0
  38. data/sample/ssl/1000cert.pem +19 -0
  39. data/sample/ssl/1000key.pem +18 -0
  40. data/sample/ssl/htdocs/index.html +10 -0
  41. data/sample/ssl/ssl_client.rb +22 -0
  42. data/sample/ssl/webrick_httpsd.rb +29 -0
  43. data/sample/stream.rb +21 -0
  44. data/sample/thread.rb +27 -0
  45. data/sample/wcat.rb +21 -0
  46. data/test/ca-chain.pem +44 -0
  47. data/test/ca.cert +23 -0
  48. data/test/client-pass.key +18 -0
  49. data/test/client.cert +19 -0
  50. data/test/client.key +15 -0
  51. data/test/helper.rb +131 -0
  52. data/test/htdigest +1 -0
  53. data/test/htpasswd +2 -0
  54. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  55. data/test/runner.rb +2 -0
  56. data/test/server.cert +19 -0
  57. data/test/server.key +15 -0
  58. data/test/sslsvr.rb +65 -0
  59. data/test/subca.cert +21 -0
  60. data/test/test_auth.rb +492 -0
  61. data/test/test_cookie.rb +309 -0
  62. data/test/test_hexdump.rb +14 -0
  63. data/test/test_http-access2.rb +508 -0
  64. data/test/test_httpclient.rb +2145 -0
  65. data/test/test_include_client.rb +52 -0
  66. data/test/test_jsonclient.rb +98 -0
  67. data/test/test_ssl.rb +562 -0
  68. data/test/test_webagent-cookie.rb +465 -0
  69. metadata +124 -0
@@ -0,0 +1,85 @@
1
+ # It is useful to re-use a HTTPClient instance for multiple requests, to
2
+ # re-use HTTP 1.1 persistent connections.
3
+ #
4
+ # To do that, you sometimes want to store an HTTPClient instance in a global/
5
+ # class variable location, so it can be accessed and re-used.
6
+ #
7
+ # This mix-in makes it easy to create class-level access to one or more
8
+ # HTTPClient instances. The HTTPClient instances are lazily initialized
9
+ # on first use (to, for instance, avoid interfering with WebMock/VCR),
10
+ # and are initialized in a thread-safe manner. Note that a
11
+ # HTTPClient, once initialized, is safe for use in multiple threads.
12
+ #
13
+ # Note that you `extend` HTTPClient::IncludeClient, not `include.
14
+ #
15
+ # require 'httpclient/include_client'
16
+ # class Widget
17
+ # extend HTTPClient::IncludeClient
18
+ #
19
+ # include_http_client
20
+ # # and/or, specify more stuff
21
+ # include_http_client('http://myproxy:8080', :method_name => :my_client) do |client|
22
+ # # any init you want
23
+ # client.set_cookie_store nil
24
+ # client.
25
+ # end
26
+ # end
27
+ #
28
+ # That creates two HTTPClient instances available at the class level.
29
+ # The first will be available from Widget.http_client (default method
30
+ # name for `include_http_client`), with default initialization.
31
+ #
32
+ # The second will be available at Widget.my_client, with the init arguments
33
+ # provided, further initialized by the block provided.
34
+ #
35
+ # In addition to a class-level method, for convenience instance-level methods
36
+ # are also provided. Widget.http_client is identical to Widget.new.http_client
37
+ #
38
+ #
39
+ require 'httpclient'
40
+
41
+ class HTTPClient
42
+ module IncludeClient
43
+
44
+
45
+ def include_http_client(*args, &block)
46
+ # We're going to dynamically define a class
47
+ # to hold our state, namespaced, as well as possibly dynamic
48
+ # name of cover method.
49
+ method_name = (args.last.delete(:method_name) if args.last.kind_of? Hash) || :http_client
50
+ args.pop if args.last == {} # if last arg was named methods now empty, remove it.
51
+
52
+ # By the amazingness of closures, we can create these things
53
+ # in local vars here and use em in our method, we don't even
54
+ # need iVars for state.
55
+ client_instance = nil
56
+ client_mutex = Mutex.new
57
+ client_args = args
58
+ client_block = block
59
+
60
+ # to define a _class method_ on the specific class that's currently
61
+ # `self`, we have to use this bit of metaprogramming, sorry.
62
+ (class << self; self ; end).instance_eval do
63
+ define_method(method_name) do
64
+ # implementation copied from ruby stdlib singleton
65
+ # to create this global obj thread-safely.
66
+ return client_instance if client_instance
67
+ client_mutex.synchronize do
68
+ return client_instance if client_instance
69
+ # init HTTPClient with specified args/block
70
+ client_instance = HTTPClient.new(*client_args)
71
+ client_block.call(client_instance) if client_block
72
+ end
73
+ return client_instance
74
+ end
75
+ end
76
+
77
+ # And for convenience, an _instance method_ on the class that just
78
+ # delegates to the class method.
79
+ define_method(method_name) do
80
+ self.class.send(method_name)
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,594 @@
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.net.InetSocketAddress'
19
+ java_import 'java.io.BufferedInputStream'
20
+
21
+ BUF_SIZE = 1024 * 16
22
+
23
+ def self.normalize_timeout(timeout)
24
+ [Java::JavaLang::Integer::MAX_VALUE, timeout].min
25
+ end
26
+
27
+ def self.connect(socket, site, opts = {})
28
+ socket_addr = InetSocketAddress.new(site.host, site.port)
29
+ if opts[:connect_timeout]
30
+ socket.connect(socket_addr, normalize_timeout(opts[:connect_timeout]))
31
+ else
32
+ socket.connect(socket_addr)
33
+ end
34
+ socket.setSoTimeout(normalize_timeout(opts[:so_timeout])) if opts[:so_timeout]
35
+ socket.setKeepAlive(true) if opts[:tcp_keepalive]
36
+ socket
37
+ end
38
+
39
+ def initialize(socket, debug_dev = nil)
40
+ @socket = socket
41
+ @debug_dev = debug_dev
42
+ @outstr = @socket.getOutputStream
43
+ @instr = BufferedInputStream.new(@socket.getInputStream)
44
+ @buf = (' ' * BUF_SIZE).to_java_bytes
45
+ @bufstr = ''
46
+ end
47
+
48
+ def close
49
+ @socket.close
50
+ end
51
+
52
+ def closed?
53
+ @socket.isClosed
54
+ end
55
+
56
+ def eof?
57
+ @socket.isClosed
58
+ end
59
+
60
+ def gets(rs)
61
+ while (size = @bufstr.index(rs)).nil?
62
+ if fill() == -1
63
+ size = @bufstr.size
64
+ break
65
+ end
66
+ end
67
+ str = @bufstr.slice!(0, size + rs.size)
68
+ debug(str)
69
+ str
70
+ end
71
+
72
+ def read(size, buf = nil)
73
+ while @bufstr.size < size
74
+ if fill() == -1
75
+ break
76
+ end
77
+ end
78
+ str = @bufstr.slice!(0, size)
79
+ debug(str)
80
+ if buf
81
+ buf.replace(str)
82
+ else
83
+ str
84
+ end
85
+ end
86
+
87
+ def readpartial(size, buf = nil)
88
+ while @bufstr.size == 0
89
+ if fill() == -1
90
+ raise EOFError.new('end of file reached')
91
+ end
92
+ end
93
+ str = @bufstr.slice!(0, size)
94
+ debug(str)
95
+ if buf
96
+ buf.replace(str)
97
+ else
98
+ str
99
+ end
100
+ end
101
+
102
+ def <<(str)
103
+ rv = @outstr.write(str.to_java_bytes)
104
+ debug(str)
105
+ rv
106
+ end
107
+
108
+ def flush
109
+ @socket.flush
110
+ end
111
+
112
+ def sync
113
+ true
114
+ end
115
+
116
+ def sync=(sync)
117
+ unless sync
118
+ raise "sync = false is not supported. This option was introduced for backward compatibility just in case."
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def fill
125
+ begin
126
+ size = @instr.read(@buf)
127
+ if size > 0
128
+ @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
129
+ end
130
+ size
131
+ rescue java.io.IOException => e
132
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
133
+ end
134
+ end
135
+
136
+ def debug(str)
137
+ @debug_dev << str if @debug_dev && str
138
+ end
139
+ end
140
+
141
+ class JRubySSLSocket < JavaSocketWrap
142
+ java_import 'java.io.ByteArrayInputStream'
143
+ java_import 'java.io.InputStreamReader'
144
+ java_import 'java.net.Socket'
145
+ java_import 'java.security.KeyStore'
146
+ java_import 'java.security.cert.Certificate'
147
+ java_import 'java.security.cert.CertificateFactory'
148
+ java_import 'javax.net.ssl.KeyManagerFactory'
149
+ java_import 'javax.net.ssl.SSLContext'
150
+ java_import 'javax.net.ssl.SSLSocketFactory'
151
+ java_import 'javax.net.ssl.TrustManager'
152
+ java_import 'javax.net.ssl.TrustManagerFactory'
153
+ java_import 'javax.net.ssl.X509TrustManager'
154
+ java_import 'org.jruby.ext.openssl.x509store.PEMInputOutput'
155
+
156
+ class JavaCertificate
157
+ attr_reader :cert
158
+
159
+ def initialize(cert)
160
+ @cert = cert
161
+ end
162
+
163
+ def subject
164
+ @cert.getSubjectDN
165
+ end
166
+
167
+ def to_text
168
+ @cert.toString
169
+ end
170
+
171
+ def to_pem
172
+ '(not in PEM format)'
173
+ end
174
+ end
175
+
176
+ class SSLStoreContext
177
+ attr_reader :current_cert, :chain, :error_depth, :error, :error_string
178
+
179
+ def initialize(current_cert, chain, error_depth, error, error_string)
180
+ @current_cert, @chain, @error_depth, @error, @error_string =
181
+ current_cert, chain, error_depth, error, error_string
182
+ end
183
+ end
184
+
185
+ class JSSEVerifyCallback
186
+ def initialize(verify_callback)
187
+ @verify_callback = verify_callback
188
+ end
189
+
190
+ def call(is_ok, chain, error_depth = -1, error = -1, error_string = '(unknown)')
191
+ if @verify_callback
192
+ ruby_chain = chain.map { |cert|
193
+ JavaCertificate.new(cert)
194
+ }.reverse
195
+ # NOTE: The order depends on provider implementation
196
+ ruby_chain.each do |cert|
197
+ is_ok = @verify_callback.call(
198
+ is_ok,
199
+ SSLStoreContext.new(cert, ruby_chain, error_depth, error, error_string)
200
+ )
201
+ end
202
+ end
203
+ is_ok
204
+ end
205
+ end
206
+
207
+ class VerifyNoneTrustManagerFactory
208
+ class VerifyNoneTrustManager
209
+ include X509TrustManager
210
+
211
+ def initialize(verify_callback)
212
+ @verify_callback = JSSEVerifyCallback.new(verify_callback)
213
+ end
214
+
215
+ def checkServerTrusted(chain, authType)
216
+ @verify_callback.call(true, chain)
217
+ end
218
+
219
+ def checkClientTrusted(chain, authType); end
220
+ def getAcceptedIssuers; end
221
+ end
222
+
223
+ def initialize(verify_callback = nil)
224
+ @verify_callback = verify_callback
225
+ end
226
+
227
+ def init(trustStore)
228
+ @managers = [VerifyNoneTrustManager.new(@verify_callback)].to_java(X509TrustManager)
229
+ end
230
+
231
+ def getTrustManagers
232
+ @managers
233
+ end
234
+ end
235
+
236
+ class SystemTrustManagerFactory
237
+ class SystemTrustManager
238
+ include X509TrustManager
239
+
240
+ def initialize(original, verify_callback)
241
+ @original = original
242
+ @verify_callback = JSSEVerifyCallback.new(verify_callback)
243
+ end
244
+
245
+ def checkServerTrusted(chain, authType)
246
+ is_ok = false
247
+ excn = nil
248
+ # TODO can we detect the depth from excn?
249
+ error_depth = -1
250
+ error = nil
251
+ error_message = nil
252
+ begin
253
+ @original.checkServerTrusted(chain, authType)
254
+ is_ok = true
255
+ rescue java.security.cert.CertificateException => excn
256
+ is_ok = false
257
+ error = excn.class.name
258
+ error_message = excn.getMessage
259
+ end
260
+ is_ok = @verify_callback.call(is_ok, chain, error_depth, error, error_message)
261
+ unless is_ok
262
+ excn ||= OpenSSL::SSL::SSLError.new('verifycallback failed')
263
+ raise excn
264
+ end
265
+ end
266
+
267
+ def checkClientTrusted(chain, authType); end
268
+ def getAcceptedIssuers; end
269
+ end
270
+
271
+ def initialize(verify_callback = nil)
272
+ @verify_callback = verify_callback
273
+ end
274
+
275
+ def init(trust_store)
276
+ tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
277
+ tmf.java_method(:init, [KeyStore]).call(trust_store)
278
+ @original = tmf.getTrustManagers.find { |tm|
279
+ tm.is_a?(X509TrustManager)
280
+ }
281
+ @managers = [SystemTrustManager.new(@original, @verify_callback)].to_java(X509TrustManager)
282
+ end
283
+
284
+ def getTrustManagers
285
+ @managers
286
+ end
287
+ end
288
+
289
+ module PEMUtils
290
+ def self.read_certificate(pem)
291
+ cert = pem.sub(/.*?-----BEGIN CERTIFICATE-----/m, '').sub(/-----END CERTIFICATE-----.*?/m, '')
292
+ der = cert.unpack('m*').first
293
+ cf = CertificateFactory.getInstance('X.509')
294
+ cf.generateCertificate(ByteArrayInputStream.new(der.to_java_bytes))
295
+ end
296
+
297
+ def self.read_private_key(pem, password)
298
+ if password
299
+ password = password.unpack('C*').to_java(:char)
300
+ end
301
+ PEMInputOutput.read_private_key(InputStreamReader.new(ByteArrayInputStream.new(pem.to_java_bytes)), password)
302
+ end
303
+ end
304
+
305
+ class KeyStoreLoader
306
+ PASSWORD = 16.times.map { rand(256) }.to_java(:char)
307
+
308
+ def initialize
309
+ @keystore = KeyStore.getInstance('JKS')
310
+ @keystore.load(nil)
311
+ end
312
+
313
+ def add(cert_source, key_source, password)
314
+ cert_str = cert_source.respond_to?(:to_pem) ? cert_source.to_pem : File.read(cert_source.to_s)
315
+ cert = PEMUtils.read_certificate(cert_str)
316
+ @keystore.setCertificateEntry('client_cert', cert)
317
+ key_str = key_source.respond_to?(:to_pem) ? key_source.to_pem : File.read(key_source.to_s)
318
+ key_pair = PEMUtils.read_private_key(key_str, password)
319
+ @keystore.setKeyEntry('client_key', key_pair.getPrivate, PASSWORD, [cert].to_java(Certificate))
320
+ end
321
+
322
+ def keystore
323
+ @keystore
324
+ end
325
+ end
326
+
327
+ class TrustStoreLoader
328
+ attr_reader :size
329
+
330
+ def initialize
331
+ @trust_store = KeyStore.getInstance('JKS')
332
+ @trust_store.load(nil)
333
+ @size = 0
334
+ end
335
+
336
+ def add(cert_source)
337
+ return if cert_source == :default
338
+ if cert_source.respond_to?(:to_pem)
339
+ pem = cert_source.to_pem
340
+ load_pem(pem)
341
+ elsif File.directory?(cert_source)
342
+ warn("#{cert_source}: directory not yet supported")
343
+ return
344
+ else
345
+ pem = nil
346
+ File.read(cert_source).each_line do |line|
347
+ case line
348
+ when /-----BEGIN CERTIFICATE-----/
349
+ pem = ''
350
+ when /-----END CERTIFICATE-----/
351
+ load_pem(pem)
352
+ # keep parsing in case where multiple certificates in a file
353
+ else
354
+ if pem
355
+ pem << line
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end
361
+
362
+ def trust_store
363
+ if @size == 0
364
+ nil
365
+ else
366
+ @trust_store
367
+ end
368
+ end
369
+
370
+ private
371
+
372
+ def load_pem(pem)
373
+ cert = PEMUtils.read_certificate(pem)
374
+ @size += 1
375
+ @trust_store.setCertificateEntry("cert_#{@size}", cert)
376
+ end
377
+ end
378
+
379
+ # Ported from commons-httpclient 'BrowserCompatHostnameVerifier'
380
+ class BrowserCompatHostnameVerifier
381
+ BAD_COUNTRY_2LDS = %w(ac co com ed edu go gouv gov info lg ne net or org).sort
382
+ require 'ipaddr'
383
+
384
+ def extract_sans(cert, subject_type)
385
+ sans = cert.getSubjectAlternativeNames rescue nil
386
+ if sans.nil?
387
+ return nil
388
+ end
389
+ sans.find_all { |san|
390
+ san.first.to_i == subject_type
391
+ }.map { |san|
392
+ san[1]
393
+ }
394
+ end
395
+
396
+ def extract_cn(cert)
397
+ subject = cert.getSubjectX500Principal()
398
+ if subject
399
+ subject_dn = javax.naming.ldap.LdapName.new(subject.toString)
400
+ subject_dn.getRdns.to_a.reverse.each do |rdn|
401
+ attributes = rdn.toAttributes
402
+ cn = attributes.get('cn')
403
+ if cn
404
+ if value = cn.get
405
+ return value.to_s
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ def ipaddr?(addr)
413
+ !(IPAddr.new(addr) rescue nil).nil?
414
+ end
415
+
416
+ def verify(hostname, cert)
417
+ is_ipaddr = ipaddr?(hostname)
418
+ sans = extract_sans(cert, is_ipaddr ? 7 : 2)
419
+ cn = extract_cn(cert)
420
+ if sans
421
+ sans.each do |san|
422
+ return true if match_identify(hostname, san)
423
+ end
424
+ raise OpenSSL::SSL::SSLError.new("Certificate for <#{hostname}> doesn't match any of the subject alternative names: #{sans}")
425
+ elsif cn
426
+ return true if match_identify(hostname, cn)
427
+ raise OpenSSL::SSL::SSLError.new("Certificate for <#{hostname}> doesn't match common name of the certificate subject: #{cn}")
428
+ end
429
+ raise OpenSSL::SSL::SSLError.new("Certificate subject for for <#{hostname}> doesn't contain a common name and does not have alternative names")
430
+ end
431
+
432
+ def match_identify(hostname, identity)
433
+ if hostname.nil?
434
+ return false
435
+ end
436
+ hostname = hostname.downcase
437
+ identity = identity.downcase
438
+ parts = identity.split('.')
439
+ if parts.length >= 3 && parts.first.end_with?('*') && valid_country_wildcard(parts)
440
+ create_wildcard_regexp(identity) =~ hostname
441
+ else
442
+ hostname == identity
443
+ end
444
+ end
445
+
446
+ def create_wildcard_regexp(value)
447
+ # Escape first then search '\*' for meta-char interpolation
448
+ labels = value.split('.').map { |e| Regexp.escape(e) }
449
+ # Handle '*'s only at the left-most label, exclude A-label and U-label
450
+ labels[0].gsub!(/\\\*/, '[^.]+') if !labels[0].start_with?('xn\-\-') and labels[0].ascii_only?
451
+ /\A#{labels.join('\.')}\z/i
452
+ end
453
+
454
+ def valid_country_wildcard(parts)
455
+ if parts.length != 3 || parts[2].length != 2
456
+ true
457
+ else
458
+ !BAD_COUNTRY_2LDS.include?(parts[1])
459
+ end
460
+ end
461
+ end
462
+
463
+ def self.create_socket(session)
464
+ opts = {
465
+ :connect_timeout => session.connect_timeout * 1000,
466
+ # send_timeout is ignored in JRuby
467
+ :so_timeout => session.receive_timeout * 1000,
468
+ :tcp_keepalive => session.tcp_keepalive,
469
+ :debug_dev => session.debug_dev
470
+ }
471
+ socket = nil
472
+ begin
473
+ if session.proxy
474
+ site = session.proxy || session.dest
475
+ socket = JavaSocketWrap.connect(Socket.new, site, opts)
476
+ session.connect_ssl_proxy(JavaSocketWrap.new(socket), Util.urify(session.dest.to_s))
477
+ end
478
+ new(socket, session.dest, session.ssl_config, opts)
479
+ rescue
480
+ socket.close if socket
481
+ raise
482
+ end
483
+ end
484
+
485
+ DEFAULT_SSL_PROTOCOL = (java.lang.System.getProperty('java.specification.version') == '1.7') ? 'TLSv1.2' : 'TLS'
486
+ def initialize(socket, dest, config, opts = {})
487
+ @config = config
488
+ begin
489
+ @ssl_socket = create_ssl_socket(socket, dest, config, opts)
490
+ ssl_version = java_ssl_version(config)
491
+ @ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String)) if ssl_version != DEFAULT_SSL_PROTOCOL
492
+ if config.ciphers != SSLConfig::CIPHERS_DEFAULT
493
+ @ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
494
+ end
495
+ ssl_connect(dest.host)
496
+ rescue java.security.GeneralSecurityException => e
497
+ raise OpenSSL::SSL::SSLError.new(e.getMessage)
498
+ rescue java.net.SocketTimeoutException => e
499
+ raise HTTPClient::ConnectTimeoutError.new(e.getMessage)
500
+ rescue java.io.IOException => e
501
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
502
+ end
503
+
504
+ super(@ssl_socket, opts[:debug_dev])
505
+ end
506
+
507
+ def java_ssl_version(config)
508
+ if config.ssl_version == :auto
509
+ DEFAULT_SSL_PROTOCOL
510
+ else
511
+ config.ssl_version.to_s.tr('_', '.')
512
+ end
513
+ end
514
+
515
+ def create_ssl_context(config)
516
+ unless config.cert_store_crl_items.empty?
517
+ raise NotImplementedError.new('Manual CRL configuration is not yet supported')
518
+ end
519
+
520
+ km = nil
521
+ if config.client_cert && config.client_key
522
+ loader = KeyStoreLoader.new
523
+ loader.add(config.client_cert, config.client_key, config.client_key_pass)
524
+ kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
525
+ kmf.init(loader.keystore, KeyStoreLoader::PASSWORD)
526
+ km = kmf.getKeyManagers
527
+ end
528
+
529
+ trust_store = nil
530
+ verify_callback = config.verify_callback || config.method(:default_verify_callback)
531
+ if !config.verify?
532
+ tmf = VerifyNoneTrustManagerFactory.new(verify_callback)
533
+ else
534
+ tmf = SystemTrustManagerFactory.new(verify_callback)
535
+ loader = TrustStoreLoader.new
536
+ config.cert_store_items.each do |item|
537
+ loader.add(item)
538
+ end
539
+ trust_store = loader.trust_store
540
+ end
541
+ tmf.init(trust_store)
542
+ tm = tmf.getTrustManagers
543
+
544
+ ctx = SSLContext.getInstance(java_ssl_version(config))
545
+ ctx.init(km, tm, nil)
546
+ if config.timeout
547
+ ctx.getClientSessionContext.setSessionTimeout(config.timeout)
548
+ end
549
+ ctx
550
+ end
551
+
552
+ def create_ssl_socket(socket, dest, config, opts)
553
+ ctx = create_ssl_context(config)
554
+ factory = ctx.getSocketFactory
555
+ unless socket
556
+ # Create a plain socket first to set connection timeouts on,
557
+ # then wrap it in a SSL socket so that SNI gets setup on it.
558
+ socket = javax.net.SocketFactory.getDefault.createSocket
559
+ JavaSocketWrap.connect(socket, dest, opts)
560
+ end
561
+ factory.createSocket(socket, dest.host, dest.port, true)
562
+ end
563
+
564
+ def peer_cert
565
+ @peer_cert
566
+ end
567
+
568
+ private
569
+
570
+ def ssl_connect(hostname)
571
+ @ssl_socket.startHandshake
572
+ ssl_session = @ssl_socket.getSession
573
+ @peer_cert = JavaCertificate.new(ssl_session.getPeerCertificates.first)
574
+ if $DEBUG
575
+ warn("Protocol version: #{ssl_session.getProtocol}")
576
+ warn("Cipher: #{@ssl_socket.getSession.getCipherSuite}")
577
+ end
578
+ post_connection_check(hostname)
579
+ end
580
+
581
+ def post_connection_check(hostname)
582
+ if !@config.verify?
583
+ return
584
+ else
585
+ BrowserCompatHostnameVerifier.new.verify(hostname, @peer_cert.cert)
586
+ end
587
+ end
588
+ end
589
+
590
+ SSLSocket = JRubySSLSocket
591
+
592
+ end
593
+
594
+ end