Java – How to bypass SSLProtocolException: handshake alert: unrecognized_name

With the release of Java SE 7 the Server Name Indication (SNI) extension was introduced as a default in the JSSE client. This enables TLS clients to connect to virtual servers (SNI is explained in RFC 4366 and I am not going to go into the specifics).

Since the “uprising” of SSL I have come across many B2B sites (including financial institutions) where their Ops department could not figure out how to properly configure SSL and it is very common to receive the following error:

javax.net.ssl.SSLProtocolException: handshake alert:  unrecognized_name

Although changing SSL configuration is a simple 2 minute fix, hosting providers (I am looking at you Afrihost and MWeb) are just not capable of resolving this for their clients. This leaves then only two options:

  1. Reject the integration due to SSL errors
  2. Find a Java “workaround”

In Java there are really just two options to “fix” this:

Disable SNI across the JVM

This is a quick fix if you can not change code and will affect the entire JVM. Just pass “-Djsse.enableSNIExtension=false” into the JVM to disable the JSSE SNI extension entirely.

Disable on connection level

If you have access to the URLConnection, this is super simple: We will use our own Hostnameverifier:

URLConnection urlConnection = url.openConnection();
        
// Only do this if SKIP SNI is set
if (urlConnection instanceof HttpsURLConnection) {
  HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
          
  httpsUrlConnection.setHostnameVerifier(new SSLSkipSNIHostnameVerifier());
}

The custom-class:

/**
 * 
 */
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;


/**
 * 
 * During handshaking, if the URL's hostname and the server's identification hostname mismatch, the verification mechanism can call back to implementers 
 * of this interface to determine if this connection should be allowed.
 * 
 * This allows us to treat some incorrectly configured HTTPS / SNI target servers as valid.
 * 
 * @see javax.net.ssl.HttpsURLConnection#setHostnameVerifier(HostnameVerifier)
In the past we used to pass into the JVM runtime the option "-Djsse.enableSNIExtension=false" which would disable SNI extensions completely, bypassing any handshake checks. With the rewrite of Tradefeeds, this is now problematic, as the setting would apply to all threads executing.

 * @author GNaschenweng
 */
public class SSLSkipSNIHostnameVerifier implements HostnameVerifier {

  /**
   * 
   */
  public SSLSkipSNIHostnameVerifier() {
  }

  /* 
   * We always treat SNI issues as valid. This should only be used in valid and verified cases and not set as the default host name-verifier for all connections
   * (non-Javadoc)
   * @see javax.net.ssl.HostnameVerifier#verify(java.lang.String, javax.net.ssl.SSLSession)
   */
  @Override
  public boolean verify (String hostname, SSLSession session) {

    // Return true so that we implicitly trust hostname mismatch
    return true;
  }

}

The above solution is a really elegant way of allowing to skip the SNI negotiation in “trusted” cases where you are not able to convince the source-system to properly configure SSL.