4
votes

Summary: Salesforce.com recently disabled TLSv1 for their sandbox instances(test.salesforce.com) and can only support TLSv1.1 and above for API integrations for both inbound and outbound requests.

I am using Java Axis1.0 client code with JDK 7.0 to connect (via webservice soap) to salesforce.com. I get exception "UNSUPPORTED_CLIENT: TLS 1.0 has been disabled in this organization. Please use TLS 1.1 or higher when connecting to Salesforce using https." With Java7.0 Supported Protocols:SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2 Enabled Protocols: TLSv1

`With Java8.0 when i try to connect to salesforce.com with java8 client, connection is successful.

Supported Protocols: SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2 Enabled Protocols: TLSv1, TLSv1.1, TLSv1.2`

I have to used Java 7 because our application is using it. I tried setting vm args: -Dhttps.protocols=TLSv1.1,TLSv1.2 -Ddeployment.security.SSLv2Hello=false -Ddeployment.security.SSLv3=false -Ddeployment.security.TLSv1=false -Ddeployment.security.TLSv1.1=true -Ddeployment.security.TLSv1.2=true" but no success.

can you help me to find out settings in Java7 to enable TLSv1.1?

3

3 Answers

5
votes

Found a solution:

I had to write custom JSSESocketFactory (because i am using Java webservice Axis1.0 client) and AxisProperties settings.

Something like,

public class TLSSocketSecureFactory extends JSSESocketFactory {

private final String TLS_VERSION_1_1 = "TLSv1.1";
private final String TLS_VERSION_1_2 = "TLSv1.2";

public TLSSocketSecureFactory(@SuppressWarnings("rawtypes") Hashtable attributes) {
super(attributes);
}

@Override
protected void initFactory() throws IOException {
SSLContext context;
try {
  context = SSLContext.getInstance(TLS_VERSION_1_1);
  context.init(null, null, null);
  sslFactory = context.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
  //printstacktrace or throw IOException
}
}

@Override
public Socket create(String host, int port, StringBuffer otherHeaders, BooleanHolder useFullURL) throws Exception {
if (sslFactory == null) {
  initFactory();
}
Socket s = super.create(host, port, otherHeaders, useFullURL);
((SSLSocket) s).setEnabledProtocols(new String[] {TLS_VERSION_1_1, TLS_VERSION_1_2 });
return s;
}
}

AxisProperties.setProperty("axis.socketSecureFactory",TLSSocketSecureFactory.class.getCanonicalName());

This is required only for JDK7. when application is migrated to JDK8, this class is not required. In Java8 TLSv1.1 and TLS1.2 is enabled by default.

Note: Setting VM config at server will not help here for Axis java client.

2
votes

From Salesforce documentation:

Java 7: Enable TLS 1.1 and TLS 1.2 using the https.protocols Java system property for HttpsURLConnection. To enable TLS 1.1 and TLS 1.2 on non-HttpsURLConnection connections, set the enabled protocols on the created SSLSocket and SSLEngine instances within the application source code.

2
votes

The solution above works well when the Axis transport is the default HTTPSender. Also, it's useful to know that the new socket factory can be replaced not just with AxisProperties but also with a system property, which can be passed on the command line like this:

-Dorg.apache.axis.components.net.SecureSocketFactory=your.package.TLSv12JSSESocketFactory

Here is the code for my TLSv12JSSESocketFactory:

package your.package;

import java.util.Hashtable;
import java.io.IOException;
import java.net.Socket;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import org.apache.axis.components.net.JSSESocketFactory;
import org.apache.axis.components.net.BooleanHolder;

public class TLSv12JSSESocketFactory extends JSSESocketFactory {

    private final String TLS_VERSION_1_2 = "TLSv1.2";

    public TLSv12JSSESocketFactory( @SuppressWarnings("rawtypes") Hashtable attributes ) {
        super(attributes);
    }

    @Override
    protected void initFactory() throws IOException {
        SSLContext context;
        try {
            context = SSLContext.getInstance( TLS_VERSION_1_2 );
            context.init( null, null, null );
            sslFactory = context.getSocketFactory();
        } catch ( Exception e ) {
            throw new IOException( "Could not init SSL factory with TLS context: " + TLS_VERSION_1_2, e );
        }
    }

    @Override
    public Socket create( String host, int port, StringBuffer otherHeaders, BooleanHolder useFullURL ) throws Exception {

        Socket s = super.create( host, port, otherHeaders, useFullURL );
        ((SSLSocket) s).setEnabledProtocols( new String[] { TLS_VERSION_1_2 } );

        return s;
    }
}

In my case I had to change Axis 1.4 using Apache Commons HttpClient as the transport. This involved creating a class implementing interface SecureProtocolSocketFactory. Here is my code:

package your.package;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;


public class TLSv12HttpsSocketFactory implements SecureProtocolSocketFactory
{
    private static final String TLS_VERSION_1_2 = "TLSv1.2";
    private final SecureProtocolSocketFactory base;

    public TLSv12HttpsSocketFactory( ProtocolSocketFactory base )
    {
        if ( base == null || !(base instanceof SecureProtocolSocketFactory) ) throw new IllegalArgumentException();
        this.base = (SecureProtocolSocketFactory) base;
    }

    private Socket acceptOnlyTLS12( Socket socket )
    {
        if ( socket != null && (socket instanceof SSLSocket) ) {
            ((SSLSocket) socket).setEnabledProtocols( new String[] { TLS_VERSION_1_2 } );
        }

        return socket;
    }

    @Override
    public Socket createSocket( String host, int port ) throws IOException
    {
        return acceptOnlyTLS12( base.createSocket(host, port) );
    }

    @Override
    public Socket createSocket( String host, int port, InetAddress localAddress, int localPort ) throws IOException
    {
        return acceptOnlyTLS12( base.createSocket(host, port, localAddress, localPort) );
    }

    @Override
    public Socket createSocket( String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params ) throws IOException
    {
        return acceptOnlyTLS12( base.createSocket(host, port, localAddress, localPort, params) );
    }

    @Override
    public Socket createSocket( Socket socket, String host, int port, boolean autoClose ) throws IOException
    {
        return acceptOnlyTLS12( base.createSocket(socket, host, port, autoClose) );
    }
}

But I also had to modify the org.apache.axis.transport.http.CommonsHTTPSender class (generates a few class files when compiled) like this:

// import the new socket factory
import your.package.TLSv12HttpsSocketFactory;
...
// add this at the end of the initialize() method
// setup to our custom TLSv1.2 socket factory
String scheme = "https";
Protocol baseHttps = Protocol.getProtocol( scheme );
int defaultPort = baseHttps.getDefaultPort();

ProtocolSocketFactory baseFactory = baseHttps.getSocketFactory();
ProtocolSocketFactory customFactory = new TLSv12HttpsSocketFactory( baseFactory );

Protocol customHttps = new Protocol( scheme, customFactory, defaultPort );
Protocol.registerProtocol( scheme, customHttps );