Ssl Handshake Alert: Unrecognized_Name Error Since Upgrade to Java 1.7.0

received handshake warning: unrecognized_name

Okay, I went through the source and found a way, although I'm not sure how enthusiastically I recommend it.

Although the javadoc for SSLParameters.setServerNames doesn't say so, if the value set is an empty List (with no elements), then ClientHandshaker actually sends no SNI at all. I suspect this is because the RFCs e.g. for 1.2 specify the min size as 1, prohibiting an empty list. (Compare to certificate_list in the Certificate message in TLS vs SSL; in SSL the min size was 1, and a client with no cert&key suitable for a server request didn't send the message at all, while in TLS it is 0, and a client with no suitable cert&key is explicitly specified to send a message containing an empty list.) While this is logical, since it is neither documented nor explicitly commented, I wouldn't be really happy relying on it.

Since it is pretty complicated (and fragile) to directly determine the other parameters needed, I think the best approach is to start from the existing parameters and modify, e.g. for SSLSocket:

SSLSocket s = SSLSocketFactory.getDefault() /* or other */ .createSocket("host", 443);
SSLParameters p = s.getSSLParameters();
p.setServerNames( new ArrayList<SNIServerName>() );
/* or j9+ p.setServerNames( List<SNIServerName>.of() ); */
s.setSSLParameters(p);
...

and for HttpsURLConnection, your original SSLSocketFactoryWrapper approach is quite close, except as above I would modify based on the actual parameters for the created SSLSocket and you must use the empty new ArrayList<SNIServerName>() and not .add anything to it.

Something very similar should work for Apache HttpClient, but I haven't gone through it, because I find that annoyingly like a maze of twisty little classes all alike.

PS: the source also confirms why varying sysprop jsse.enableSNIExtension won't work; that (like many others) is read and cached when JSSE is first loaded and not read subsequently. You could use reflection to break into the class and change the cached value, but let's not go there.

How to fix SSLProtocolException: handshake alert: unrecognized_name without disabling SNI

I managed to solve the issue by extending the SSLSocketConnection and by sending null instead of the hostname when the createSocket is called. That way java disables the SNI. Then I just pass a instance of the new class to Jsoup where I know the SNI will fail.

import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

public class CustomSSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory defaultFactory;
public CustomSSLSocketFactory() throws IOException {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}

public void checkServerTrusted(X509Certificate[] chain, String authType) {
}

public X509Certificate[] getAcceptedIssuers() {
return null;
}
}};

try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init((KeyManager[])null, trustAllCerts, new SecureRandom());
defaultFactory = sslContext.getSocketFactory();
} catch (KeyManagementException | NoSuchAlgorithmException var3) {
throw new IOException("Can't create unsecure trust manager");
}
}
@Override
public String[] getDefaultCipherSuites() {
return defaultFactory.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
return defaultFactory.getSupportedCipherSuites();
}

@Override
public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
//magic happens here, we send null as hostname
return defaultFactory.createSocket(socket, null, i, b);
}

@Override
public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
return defaultFactory.createSocket(s,i);
}

@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
return defaultFactory.createSocket(s,i,inetAddress,i1);
}

@Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
return defaultFactory.createSocket(inetAddress, i);
}

@Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
return defaultFactory.createSocket(inetAddress,i, inetAddress1, i1);
}
}

Jsoup initialization.

Connection conn = Jsoup.connect(url);
conn.sslSocketFactory(new CustomSSLSocketFactory());

SNI : SSLHandshakeException unrecognized_name

We found a solution by changing something in karate-apache/src/main/java/com/intuit/karate/http/apache/ApacheHttpClient.java :

SSLConnectionSocketFactory socketFactory = new LenientSslConnectionSocketFactory(sslContext, new NoopHostnameVerifier());

becomes

SSLConnectionSocketFactory socketFactory = new SslConnectionSocketFactory(sslContext, new NoopHostnameVerifier());

@Peter, do you think a parameter to use strict or lenient SSL connection can be a possibility?



Related Topics



Leave a reply



Submit