aesterline / jruby-httpclient

A Ruby wrapper for Apache HTTPClient
Apache License 2.0
23 stars 6 forks source link

Ignoring SSL verification errors #5

Open rb2k opened 11 years ago

rb2k commented 11 years ago

I use jruby-httpclient in an environment where I might run into self-signed SSL certs which usually leads to this:

SSLSessionImpl.java:352:in `getPeerCertificates': javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    from AbstractVerifier.java:128:in `verify'
    from SSLSocketFactory.java:390:in `connectSocket'
    from DefaultClientConnectionOperator.java:148:in `openConnection'
    from AbstractPoolEntry.java:149:in `open'
    from AbstractPooledConnAdapter.java:121:in `open'
    from DefaultRequestDirector.java:561:in `tryConnect'
    from DefaultRequestDirector.java:415:in `execute'
    from AbstractHttpClient.java:820:in `execute'
    from AbstractHttpClient.java:754:in `execute'
    from AbstractHttpClient.java:732:in `execute'
    from NativeMethodAccessorImpl.java:-2:in `invoke0'
    from NativeMethodAccessorImpl.java:39:in `invoke'
    from DelegatingMethodAccessorImpl.java:25:in `invoke'
    from Method.java:597:in `invoke'
    from JavaMethod.java:455:in `invokeDirectWithExceptionHandling'

I really don't care all that much about the validity of the SSL certs. Is there any way to disable the cert checking alltogether for jruby-httpclient?

rb2k commented 11 years ago

Example:

require 'rubygems'
require 'http_client'

user_agent = 'testing'
client_opts = {
  :connection_timeout => 10000,
  :timeout_in_seconds => 30,
  :user_agent => user_agent,
  :handle_redirects => true,
  :max_redirects => 10,
  :use_ssl => true
}

client = HTTP::Client.new(client_opts)
get_req = HTTP::Get.new('https://www.netuno.net/')
result = client.execute(get_req)
rb2k commented 11 years ago

This can be somewhat solved by doing this:

// Example java.security.Provider implementation
// that trusts ALL SSL certificates
// Regardless of whether they are valid or not

// Store this code in a file called MyProvider.java

import java.security.Security;
import java.security.KeyStore;
import java.security.Provider;
import java.security.cert.X509Certificate;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509TrustManager;

public class MyProvider extends Provider
{
  public MyProvider()
  {
    super("MyProvider", 1.0, "Trust certificates");
    put("TrustManagerFactory.TrustAllCertificates", MyTrustManagerFactory.class.getName());
  }

  protected static class MyTrustManagerFactory extends TrustManagerFactorySpi
  {
    public MyTrustManagerFactory()
      {}
    protected void engineInit( KeyStore keystore )
      {}
    protected void engineInit(ManagerFactoryParameters mgrparams )
      {}
    protected TrustManager[] engineGetTrustManagers()
    {
      return new TrustManager[] {new MyX509TrustManager()};
    }
  }

  protected static class MyX509TrustManager implements X509TrustManager
  {
    public void checkClientTrusted(X509Certificate[] chain, String authType)
      {}
    public void checkServerTrusted(X509Certificate[] chain, String authType)
      {}
    public X509Certificate[] getAcceptedIssuers()
      { return null; }
  }

}

then

javac MyProvider.java

And now you can do this:

java_import 'MyProvider'
java.security.Security.addProvider(MyProvider.new)
java.security.Security.setProperty("ssl.TrustManagerFactory.algorithm", "TrustAllCertificates");

This seems to somewhat work. It will however still blow up when running into a http -> https redirect

rb2k commented 11 years ago

Ok, next approach, less java :)

class TrustStrategy
  def isTrusted(chain, auth_type)
      return true;
  end
end

[...]

      http_client = HTTP::Client.new(@client_opts)      
      begin
        get_req = HTTP::Get.new(url_to_crawl.to_s)
        result = http_client.execute(get_req)
      rescue javax.net.ssl.SSLPeerUnverifiedException
        # Disable SSL
        ssl_socket_factory = org.apache.http.conn.ssl.SSLSocketFactory.new(TrustStrategy.new)
        scheme = org.apache.http.conn.scheme.Scheme.new('https', 443, ssl_socket_factory)
        http_client.instance_variable_get('@client').getConnectionManager().getSchemeRegistry().register(scheme)        
        get_req = HTTP::Get.new(url_to_crawl.to_s.gsub('http:', 'https:'))
        result = http_client.execute(get_req)
      end

Quite a hack, but it works for me for now...