From 0f04a6aafa52fe71b46f798f769ace89325e077f Mon Sep 17 00:00:00 2001 From: Matt Pasquini Date: Fri, 17 Jul 2020 08:44:41 -0700 Subject: [PATCH] SSL min_version/max_version support --- lib/httpclient/jruby_ssl_socket.rb | 3 +++ lib/httpclient/ssl_config.rb | 17 ++++++++++-- test/test_ssl.rb | 42 +++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/lib/httpclient/jruby_ssl_socket.rb b/lib/httpclient/jruby_ssl_socket.rb index 1a0db8ea..f34a821d 100644 --- a/lib/httpclient/jruby_ssl_socket.rb +++ b/lib/httpclient/jruby_ssl_socket.rb @@ -485,6 +485,9 @@ def self.create_socket(session) DEFAULT_SSL_PROTOCOL = (java.lang.System.getProperty('java.specification.version') == '1.7') ? 'TLSv1.2' : 'TLS' def initialize(socket, dest, config, opts = {}) @config = config + raise NotImplementedError.new('SSL min_version is not yet supported by jruby') if config.min_version + raise NotImplementedError.new('SSL max_version is not yet supported by jruby') if config.max_version + begin @ssl_socket = create_ssl_socket(socket, dest, config, opts) ssl_version = java_ssl_version(config) diff --git a/lib/httpclient/ssl_config.rb b/lib/httpclient/ssl_config.rb index 472db9d0..aecf0de4 100644 --- a/lib/httpclient/ssl_config.rb +++ b/lib/httpclient/ssl_config.rb @@ -95,8 +95,17 @@ def attr_config(symbol) # String name of OpenSSL's SSL version method name: TLSv1_2, TLSv1_1, TLSv1, # SSLv2, SSLv23, SSLv3 or :auto (and nil) to allow version negotiation (default). # See {OpenSSL::SSL::SSLContext::METHODS} for a list of available versions - # in your specific Ruby environment. + # in your specific Ruby environment. This is + # deprecated and only provided for backwards compatibility. Use + # #min_version= and #max_version= instead. attr_config :ssl_version + # Sets the upper bound on the supported SSL/TLS protocol version. + # See min_version for possible values. + attr_config :max_version + # Sets the lower bound on the supported SSL/TLS protocol version. + # The version may be specified by an integer constant named + # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version". + attr_config :min_version # OpenSSL::X509::Certificate:: certificate for SSL client authentication. # nil by default. (no client authentication) attr_config :client_cert @@ -125,7 +134,7 @@ def attr_config(symbol) # A number of OpenSSL's SSL options. Default value is # OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2 # CAUTION: this is OpenSSL specific option and ignored on JRuby. - # Use ssl_version to specify the TLS version you want to use. + # Use min_version and max_version to specify the TLS versions you want to use. attr_config :options # A String of OpenSSL's cipher configuration. Default value is # ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH @@ -158,6 +167,8 @@ def initialize(client) @dest = nil @timeout = nil @ssl_version = :auto + @max_version = nil + @min_version = nil # Follow ruby-ossl's definition @options = OpenSSL::SSL::OP_ALL @options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) @@ -309,6 +320,8 @@ def set_context(ctx) # :nodoc: ctx.options = @options ctx.ciphers = @ciphers ctx.ssl_version = @ssl_version unless @ssl_version == :auto + ctx.min_version = @min_version if @min_version + ctx.max_version = @max_version if @max_version end # post connection check proc for ruby < 1.8.5. diff --git a/test/test_ssl.rb b/test/test_ssl.rb index a58ea822..3e439e37 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -251,7 +251,7 @@ def test_no_sslv3 omit('TODO: SSLv3 is not supported in many environments. re-enable when disable TLSv1') teardown_server setup_server_with_ssl_version(:SSLv3) - assert_raise(OpenSSL::SSL::SSLError) do + assert_raise(NotImplementedError) do @client.ssl_config.verify_mode = nil @client.get("https://localhost:#{serverport}/hello") end @@ -266,6 +266,19 @@ def test_allow_tlsv1 end end + def test_allow_with_min_max + if RUBY_ENGINE == 'jruby' + omit('jruby does not currently support setting OpenSSL min_version or max_version') + else + teardown_server + setup_server_with_min_and_max_version(:TLS1_2) + assert_nothing_raised do + @client.ssl_config.verify_mode = nil + @client.get("https://localhost:#{serverport}/hello") + end + end + end + def test_use_higher_TLS omit('TODO: it does not pass with Java 7 or old openssl') teardown_server @@ -500,6 +513,33 @@ def setup_server_with_ssl_version(ssl_version) @server_thread = start_server_thread(@server) end + def setup_server_with_min_and_max_version(version) + logger = Logger.new(STDERR) + logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level) + @server = WEBrick::HTTPServer.new( + :BindAddress => "localhost", + :Logger => logger, + :Port => 0, + :AccessLog => [], + :DocumentRoot => DIR, + :SSLEnable => true, + :SSLCACertificateFile => File.join(DIR, 'ca.cert'), + :SSLCertificate => cert('server.cert'), + :SSLPrivateKey => key('server.key') + ) + @server.ssl_context.min_version = version + @server.ssl_context.max_version = version + + @serverport = @server.config[:Port] + [:hello].each do |sym| + @server.mount( + "/#{sym}", + WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc) + ) + end + @server_thread = start_server_thread(@server) + end + def setup_server_with_server_cert(ca_cert, server_cert, server_key) logger = Logger.new(STDERR) logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)