logo Java Proxy & SSL

While security requirement increase in enterprises more and more Proxies will need special care. Let’s see up to date practices to manage them.

HTTP proxy in java

https://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html#Proxies

JVM Proxy

This will force the whole JVM to use proxy, including database access (unless excluded using nonProxyHosts)

For https URL

System.setProperty("https.proxyHost", "172.27.0.254");
System.setProperty("https.proxyPort", "8080");
System.setProperty("https.nonProxyHosts", "localhost|127.*|[::1]");

Or using command line :

-Dhttps.proxyHost=172.27.0.254 -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts="localhost|127.*|[::1]"

For http URL

System.setProperty("http.proxyHost", "172.27.0.254");
System.setProperty("http.proxyPort", "8080");
System.setProperty("http.nonProxyHosts", "localhost|127.*|[::1]");

Can also be used from command line : -Dhttp.proxyHost=172.27.0.254 -Dhttp.proxyPort=8080

Proxy with authentication

When you receive a ‘407: Proxy Authentication Required’, you may need Proxy authentication :

Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        if (getRequestorType() == RequestorType.PROXY) {
            String prot = getRequestingProtocol().toLowerCase();
            String host = System.getProperty(prot + ".proxyHost", "");
            String port = System.getProperty(prot + ".proxyPort", "0");
            String user = System.getProperty(prot + ".proxyUser", "");
            String password = System.getProperty(prot + ".proxyPassword", "");
            if (getRequestingHost().equalsIgnoreCase(host)) {
                if (Integer.parseInt(port) == getRequestingPort()) {
                    return new PasswordAuthentication(user, password.toCharArray());
                }
            }
        }
        return null;
    }
});

Once again this will impact the whole JVM and should be called once only.

System properties (proxyUser, proxyPassword) does not work :

    @Test
    public void test_HttpURLConnection_setProperty_auth() throws IOException {
        // Required for JDK8.111+
        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
        System.setProperty("jdk.http.auth.proxying.disabledSchemes", "");
        //
        URL url = new URL("https://www.example.com");
        //
        System.setProperty("https.proxyHost", "172.27.0.254");
        System.setProperty("https.proxyPort", "8080");
        System.setProperty("https.proxyUser", "user");
        System.setProperty("https.proxyPassword", "pass");
        // Could be used
        System.setProperty("http.proxyHost", "172.27.0.254");
        System.setProperty("http.proxyPort", "8080");
        System.setProperty("http.proxyUser", "user");
        System.setProperty("http.proxyPassword", "pass");
        // 
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        /**
         * As an evidence, http*.proxyUser does not work
         */
        assertEquals(407, con.getResponseCode());
        assertEquals("Proxy Authentication Required", con.getResponseMessage());
    }

URLConnection

java.net.Proxy

When using URLConnection a better way is possible using java.net.Proxy (which does not force the whole JVM) :

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("172.27.0.254", 8080);
URL url = new URL("https://www.example.com);
HttpURLConnection con = (HttpURLConnection) url.openConnection(proxy);

This can be used with a default Authenticator.

See https://rolandtapken.de/blog/2012-04/java-process-httpproxyuser-and-httpproxypassword

SOAPConnection

You can create to own SOAP request without any framework using SOAPConnection

SOAPConnection soapConnection = SOAPConnectionFactory.newInstance().createConnection();
SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), "https://salesforce.com/services/Soap/u/41.0");

But the SOAPConnectionFactory returns a com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection which does not use java.net.Proxy :

    private HttpURLConnection createConnection(URL endpoint) throws IOException {
        return (HttpURLConnection)endpoint.openConnection();
    }

So you have to use System properties to specify a Proxy server (hence impacting all JVM or playing with exclusions).

Apache

CXF

JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.setAddress("https://endpoint.soap");
jaxWsProxyFactoryBean.setServiceClass(MyInterface.class);
MyInterface myService = (MyInterface) jaxWsProxyFactoryBean.create();
Client client = ClientProxy.getClient(myService);

HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = httpConduit.getClient();
httpClientPolicy.setProxyServer("172.27.0.254");
httpClientPolicy.setProxyServerPort(8080);
httpClientPolicy.setProxyServerType(ProxyServerType.HTTP);

// If proxy Authentication :
httpConduit.getProxyAuthorization().setUserName("user");
httpConduit.getProxyAuthorization().setPassword("passw");

Note : Spring can help you to configure your HTTPConduit : see http://cxf.apache.org/docs/client-http-transport-including-ssl-support.html#ClientHTTPTransport(includingSSLsupport)-Theconduitelement

HttpClient

HttpClient is used by CXF, you’ll have to get the HTTPConduit too to configure your Proxy settings.

Spring Rest Template


    private final RestTemplate getProxiedRestTemplate() {
        RestTemplate r = null;
        if (isProxyActivated()) {
            HttpClientBuilder client = HttpClientBuilder.create();
            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
            client.setProxy(new HttpHost("172.27.0.254", 8080));
            if (withAuthentication()) {
                Credentials credentials = new UsernamePasswordCredentials(getProxyUsername(), getProxyPassword());
                AuthScope authScope = new AuthScope("172.27.0.254", 8080);
                CredentialsProvider credsProvider = new BasicCredentialsProvider();
                credsProvider.setCredentials(authScope, credentials);
                client.setDefaultCredentialsProvider(credsProvider);
            }
            requestFactory.setHttpClient(client.build());
            r = new RestTemplate(requestFactory);
        } else {
            r = new RestTemplate();
        }
        return r;
    }

JDK 8.111+

http://www.oracle.com/technetwork/java/javase/8u111-relnotes-3124969.html

Disable Basic authentication for HTTPS tunneling In some environments, certain authentication schemes may be undesirable when proxying HTTPS. Accordingly, the Basic authentication scheme has been deactivated, by default, in the Oracle Java Runtime, by adding Basic to the jdk.http.auth.tunneling.disabledSchemes networking property. Now, proxies requiring Basic authentication when setting up a tunnel for HTTPS will no longer succeed by default. If required, this authentication scheme can be reactivated by removing Basic from the jdk.http.auth.tunneling.disabledSchemes networking property, or by setting a system property of the same name to “” ( empty ) on the command line.

Additionally, the jdk.http.auth.tunneling.disabledSchemes and jdk.http.auth.proxying.disabledSchemes networking properties, and system properties of the same name, can be used to disable other authentication schemes that may be active when setting up a tunnel for HTTPS, or proxying plain HTTP, respectively. JDK-8160838 (not public)

System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
System.setProperty("jdk.http.auth.proxying.disabledSchemes", "");

Or as a JVM parameter in your command line :

-Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.http.auth.proxying.disabledSchemes=""

Example to start your jetty server :

vi /etc/default/jetty

JAVA_OPTIONS="$JAVA_OPTIONS -Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.http.auth.proxying.disabledSchemes="" "

SSL troubleshooting

Enable logs :

System.setProperty("javax.net.debug", "all");// "ssl:record:plaintext"
sun.util.logging.PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection").setLevel(sun.util.logging.PlatformLogger.Level.ALL);
sun.util.logging.PlatformLogger.getLogger("sun.net.www.protocol.http.HttpsURLConnection").setLevel(sun.util.logging.PlatformLogger.Level.ALL);

Enable old insecure protocols :

System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2,SSLv3");
(681 mots)