HttpClient: ssl handshake on every request - java

I use static HttpClient, and it works very slowly over https. I have added -Djavax.net.debug=ssl and found that handshaking is started for every https request again.
looks like it can not reuse old session, but I can not found why.
9007199254743735, setSoTimeout(0) called
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
9007199254743735, setSoTimeout(0) called
%% No cached client session
*** ClientHello, SSLv3
...
%% Didn't cache non-resumable client session: [Session-1, SSL_RSA_WITH_RC4_128_MD5]
...
Is initial handshake: true
BTW. before I was faced with another problem on this host: "Received fatal alert: bad_record_mac", it was solved by allowing only SSLv3
UPD1: HttpClient init code
final SSLContext sslCtx;
sslCtx = SSLContext.getInstance("SSL");
sslCtx.init(null, new TrustManager[]{new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] cert,
String authType) {
}
#Override
public void checkServerTrusted(X509Certificate[] cert,
String authType) {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}}, null);
X509HostnameVerifier verifier = new X509HostnameVerifier() {
#Override
public void verify(String string, SSLSocket ssls) throws IOException {
}
#Override
public void verify(String string, X509Certificate xc) throws SSLException {
}
#Override
public void verify(String string, String[] strings, String[] strings1) throws SSLException {
}
#Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
};
final SSLSocketFactory socketFactory = new SSLv3SocketFactory(sslCtx, verifier);
final SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", 443, socketFactory));
final PoolingClientConnectionManager cm = new PoolingClientConnectionManager(registry);
cm.setMaxTotal(100);
cm.setDefaultMaxPerRoute(50);
final HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(httpParams, timeout);
httpClient = new DefaultHttpClient(cm, httpParams);
((DefaultHttpClient) httpClient).setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
#Override
public long getKeepAliveDuration(HttpResponse hr, HttpContext hc) {
return 0;
}
});
httpClient.getParams().setParameter("http.socket.timeout", 900000);
UPD2: modified SSLSocketFactory("Received fatal alert: bad_record_mac" issue)
public class SSLv3SocketFactory extends SSLSocketFactory {
private final javax.net.ssl.SSLSocketFactory socketfactory;
public SSLv3SocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) {
super(sslContext, hostnameVerifier);
this.socketfactory = sslContext.getSocketFactory();
}
#Override
public Socket createLayeredSocket(
final Socket socket,
final String host,
final int port,
final boolean autoClose) throws IOException, UnknownHostException {
SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
socket,
host,
port,
autoClose);
sslSocket.setEnabledProtocols(new String[]{"SSLv3"});
return sslSocket;
}
#Override
public Socket connectSocket(
final Socket socket,
final InetSocketAddress remoteAddress,
final InetSocketAddress localAddress,
final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
if (socket instanceof SSLSocket) {
((SSLSocket) socket).setEnabledProtocols(new String[]{"SSLv3"});;
}
return super.connectSocket(socket, remoteAddress, localAddress, params);
}
}
UPD3: Problem exists only for SSLv3, TLSv1 works fine

HttpClient re-uses persistent SSL connections with client authentication only if it can make sure they belong to the same user / security context (for obvious reasons).
Make sure you are using the same HttpContext for all logically related requests. This will ensure the security principal (DN of the client certificate) will get propagated between individual HTTP requests.
Follow-up
It tuned out the server simply does not want connections to be re-used. Every response contains 'Connection: close' directive that prompts the client to close connections after receiving the response. It may happen, though, that the server treats different clients differently based on the request message composition. Try masquerading HttpClient by using a different User-Agent header value and see if that makes any difference.

As you state in a comment that the problem only occurs with one server, clearly the problem is at that server. They have set a very short SSL session timeout, or disabled session resumption altogether somehow.
There's nothing you can do about it from your end.

Related

How can I get hold of the server's certificate at run time when opening an SSLSocket in a Java client?

I'm writing a Gemini client in Java. Two parts of the Gemini spec are relevant here:
Servers MUST use TLS...
...the strongly RECOMMENDED approach is to implement a lightweight "TOFU" certificate-pinning system which treats self-signed certificates as first-class citizens.
By following this answer How do I accept a self-signed certificate with a Java using SSLSocket I can successfully connect to Gemini servers with self-signed certificates.
But to implement the "TOFU" (trust on first use) requirement I should get hold of the server's certificate to validate that it hasn't changed on subsequent requests. How do I get the certificate?
The code I have is:
// On startup
try{
m_sslContext = SSLContext.getInstance( "TLS" );
m_sslContext.init(
null,
new TrustManager[]{
new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){
return new X509Certificate[] {};
}
public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException
{}
public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException
{}
}
},
null
);
}
catch( NoSuchAlgorithmException | KeyManagementException x )
{
// error handling
}
// On fetching a Gemini URL:
Socket socket = m_sslContext.getSocketFactory().createSocket( host, port );
// ...read and write the socket
You can store every server certificate and get it afterwords with a custom TrustManager.
An example setup would be:
public class MyTrustManager implements X509TrustManager {
private final Map<Integer, List<X509Certificate>> serverCertificates = new HashMap<>();
private int counter = 0;
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
serverCertificates.put(counter++, Arrays.asList(x509Certificates));
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public Map<Integer, List<X509Certificate>> getServerCertificates() {
return Collections.unmodifiableMap(serverCertificates);
}
}
Usage
SSLContext sslContext = SSLContext.getInstance( "TLS" );
MyTrustManager myTrustManager = new MyTrustManager();
sslContext.init(null, new TrustManager[]{myTrustManager}, null);
// execute some https requests...
// and get the cached server certificates
Map<Integer, List<X509Certificate>> serverCertificates = myTrustManager.getServerCertificates();

How can I preserve the order of my ciphersuite list in OkHttp?

I am unable to preserve the order of my ciphersuite list. I have set the order to be in my preferred way however, when the request is made and I check which order the ciphersuites are in, it is slightly different from my original. I am wondering if possibly okhttp is reordering them via a MAP structure or something else. If they are, I would like to learn where this is happening to be able to adjust it to preserve the order.
My connection spec used:
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_1, TlsVersion.TLS_1_2,TlsVersion.TLS_1_3)
.cipherSuites(
CipherSuite.TLS_AES_128_GCM_SHA256,
CipherSuite.TLS_AES_256_GCM_SHA384,
CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
)
.build();
I want to also share that I am using conscrypt which enables these cipher suites. They are all used by okhttp but not in the correct order.
This looks like a bug in OkHttp https://github.com/square/okhttp/issues/6390
/**
* Returns an array containing only elements found in this array and also in [other]. The returned
* elements are in the same order as in this.
*/
fun Array<String>.intersect(
other: Array<String>,
comparator: Comparator<in String>
)
private fun supportedSpec(sslSocket: SSLSocket, isFallback: Boolean): ConnectionSpec {
var cipherSuitesIntersection = if (cipherSuitesAsString != null) {
sslSocket.enabledCipherSuites.intersect(cipherSuitesAsString, CipherSuite.ORDER_BY_NAME)
} else {
sslSocket.enabledCipherSuites
}
https://www.rfc-editor.org/rfc/rfc5246
The cipher suite list, passed from the client to the server in the
ClientHello message, contains the combinations of cryptographic
algorithms supported by the client in order of the client's
preference (favorite choice first). Each cipher suite defines a key
exchange algorithm, a bulk encryption algorithm (including secret key
length), a MAC algorithm, and a PRF. The server will select a cipher
suite or, if no acceptable choices are presented, return a handshake
failure alert and close the connection. If the list contains cipher
suites the server does not recognize, support, or wish to use, the
server MUST ignore those cipher suites, and process the remaining
ones as usual.
Yuri already mentioned that this is a bug within OkHttp. I am not quite sure how fast they will fix it but as an alternative you can still get it working with a delegate sslsocketfactory instead of using the ConnectionSpec
An example would setup would be:
Your SSL properties
SSLParameters sslParameters = new SSLParameters();
sslParameters.setProtocols(new String[]{"TLSv1.3", "TLSv1.2", "TLSv1.1"});
sslParameters.setCipherSuites(new String[]{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA"
});
Your custom SSLSocketFactory
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public final class CompositeSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory sslSocketFactory;
private final SSLParameters sslParameters;
public CompositeSSLSocketFactory(SSLSocketFactory sslSocketFactory, SSLParameters sslParameters) {
this.sslSocketFactory = sslSocketFactory;
this.sslParameters = sslParameters;
}
#Override
public String[] getDefaultCipherSuites() {
return sslParameters.getCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return sslParameters.getCipherSuites();
}
#Override
public Socket createSocket() throws IOException {
Socket socket = sslSocketFactory.createSocket();
return withSslParameters(socket);
}
#Override
public Socket createSocket(Socket socket, InputStream inputStream, boolean autoClosable) throws IOException {
Socket newSocket = sslSocketFactory.createSocket(socket, inputStream, autoClosable);
return withSslParameters(newSocket);
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClosable) throws IOException {
Socket newSocket = sslSocketFactory.createSocket(socket, host, port, autoClosable);
return withSslParameters(newSocket);
}
#Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
Socket socket = sslSocketFactory.createSocket(host, port);
return withSslParameters(socket);
}
#Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
Socket socket = sslSocketFactory.createSocket(host, port, localAddress, localPort);
return withSslParameters(socket);
}
#Override
public Socket createSocket(InetAddress address, int port) throws IOException {
Socket socket = sslSocketFactory.createSocket(address, port);
return withSslParameters(socket);
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
Socket socket = sslSocketFactory.createSocket(address, port, localAddress, localPort);
return withSslParameters(socket);
}
private Socket withSslParameters(Socket socket) {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setSSLParameters(sslParameters);
}
return socket;
}
}
Your OkHttp client configuration
SSLContext sslContext = ...; //your already initialised SSLContext
X509TrustManager trustManager = ...; //your already initialised TrustManager
SSLSocketFactory baseSocketFactory = sslContext.getSocketFactory();
SSLSocketFactory customSocketFactory = new CompositeSSLSocketFactory(baseSocketFactory, sslParameters);
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(customSocketFactory, trustManager)
.build();
Yes it is verbose, however it will keep the order as you defined :) I would advise to wait for the bug-fix, but if you want to fix it by your self for the time being than this would be an option.

https taking long to run on production mode

So i hit a https request to other server.I have reduced its time by adding set Timeout property which timeouts after 5 seconds in case response does not come from server.Problem is setup is working fine on dev mode but in production mode timeout do not work and it takes 4-5 minutes to get response from server.
USE CASE:
i am not sending username and password while connecting to server as i need to catch the exception and give appropriate message to user.I am using XmlRpcClientConfigImpl client and TrustManager and SSLContext of java.net for handshaking.
HAve done some logging also in which code inside exception runs after 4-5 mins
Refered from setTimeout using xmlprc
I am using jetty as server
Here is some code as i can not share much of it
connected =true;
Proxy proxy = new Proxy(ServerVO.getHost(), ServerVO.getPort(), ServerVO.getUserName(), ServerVO.getPassword(), isHTTP);
int defaultConnTimeOut = SomeCass.getConnectionTimeOut();
int defaultReplyTimeOut = SomeCass.getReplyTimeOut();
SomeCass.setConnectionTimeOut(2000);
SomeCass.setReplyTimeOut(5000);
try {
logger.info("fetching versionnnnnnnn-----------------");
SomeCass.version();
}
catch (Exception e) {
logger.info("fetching versionnnnnnnn-------exceptionnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn----------");
connected = Boolean.FALSE;
}
finally {
SomeCass.setConnectionTimeOut(defaultConnTimeOut);
SomeCass.setReplyTimeOut(defaultReplyTimeOut);
}
code for timeout
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
XmlRpcClient client = new XmlRpcClient();
config.setServerURL(new URL(serverURL));
config.setConnectionTimeout(xmlrpcConnTimeout);
config.setReplyTimeout(xmlrpcReplyTimeOut);
client.setConfig(config);
//setting host username everything in config
Code for handshake on Proxy class consturctor
this.address = address;
this.port = port;
this.username = username;
this.password = password;
this.url = url;
if(!this.isHTTP) {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
// Trust always
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
// Trust always
}
} };
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
// Create empty HostnameVerifier
HostnameVerifier hv = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
Found a solution for this
Added this property to XMLRPCclient
client.setTransportFactory(new XmlRpcCommonsTransportFactory(client));
It creates a new instance for every request
https still taking 5 sec extra than http but i guess it is the expected behavior as https is generaly slower than http.Still if anyone can suggest how to reduce this time further would be a great help

Android 4.4.2 SSL handshake aborted

Code works on my Genymotion Android 4.4.4 emulator but not on the device i'm using (4.4.2).
I've tried lots of "trust all certificate" workarounds but to no avail (I don't think this is the issue anyway, as the certificate is AOK).
I think I have identified the cipher (using a wireshark trace from my desktop); TLS 0x00 0x1E which appears to be somewhat rare?
Any idea how to fix?
Here's my code
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();
String baseURL = "https://mysite.co.uk/api/";
HttpGetHC4 request = new HttpGetHC4(baseURL + "/authenticate?user=abcd&password=1234");
CloseableHttpResponse response = client.execute(request);
And error;
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:449)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:340)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:281)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:124)
at org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:322)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:373)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:225)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:178)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.java:239)
at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.java:189)
at android.os.AsyncTask.finish(AsyncTask.java:632)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:406)
... 25 more
EDIT
Trying using a different technique/library;
RequestQueue queue = Volley.newRequestQueue(this);
String url ="https://mysite.co.uk/api/authenticate?user=abcd&password=1234";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
Log.d("response: ", response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("response: ", error.toString());
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
Returns;
D/response:: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ad51be0: Failure in SSL library, usually a protocol error
D/response:: error:140740B5:SSL routines:SSL23_CLIENT_HELLO:no ciphers available (external/openssl/ssl/s23_clnt.c:486 0x684dfce0:0x00000000)
Or with the NoSSLv3SocketFactory hack from Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error;
HttpStack stack = new HurlStack(null, new NoSSLv3SocketFactory());
RequestQueue queue = Volley.newRequestQueue(this, stack);
Returns;
D/response:: com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ae51d30: Failure in SSL library, usually a protocol error
D/response:: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
Android uses different protocols for network operations.
Default configuration for different Android versions.
I found solution for OkHttpClient.
Protocol Supported (API Levels) Enabled by default (API Levels)
SSLv3 1–25 1–22
TLSv1 1+ 1+
TLSv1.1 16+ 20+
TLSv1.2 16+ 20+
So we have to change Protocol for connection in Android VERSION >= 16 & VERSION < 22.
Create java file Tls12SocketFactory.java
/**
* Enables TLS v1.2 when creating SSLSockets.
* <p/>
* For some reason, android supports TLS v1.2 from API 16, but enables it by
* default only from API 20.
* #link https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
* #see SSLSocketFactory
*/
public class Tls12SocketFactory extends SSLSocketFactory {
private static final String[] TLS_V12_ONLY = {"TLSv1.2"};
final SSLSocketFactory delegate;
public Tls12SocketFactory(SSLSocketFactory base) {
this.delegate = base;
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patch(delegate.createSocket(s, host, port, autoClose));
}
#Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return patch(delegate.createSocket(host, port, localHost, localPort));
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patch(delegate.createSocket(host, port));
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patch(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patch(Socket s) {
if (s instanceof SSLSocket) {
((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY);
}
return s;
}
}
Put this method some where in your code.
public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2).build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);
client.connectionSpecs(specs);
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
}
}
return client;
}
public OkHttpClient getNewHttpClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder().followRedirects(true).followSslRedirects(true)
.retryOnConnectionFailure(true).cache(null).connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS);
return enableTls12OnPreLollipop(client).build();
}
Create OkHttp instance like below :
private OkHttpClient getNewHttpClient() {
OkHttpClient.Builder client = new OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cache(null)
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS);
return enableTls12OnPreLollipop(client).build();
}
Use client object like this:
OkHttpClient client = getNewHttpClient();
Request.Builder requestBuilder = new Request.Builder();
URL url = new URL("YOUR_URL_LINK");
Request request = requestBuilder.url(url).build();
Call call = client.newCall(request);
Response response = call.execute();
THANKS
Try this:
public class ClientSSLSocketFactory {
public static SSLSocketFactory getSocketFactory(Context context) {
try {
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{tm}, new SecureRandom());
SSLSocketFactory ssf = SSLCertificateSocketFactory.getDefault(10000, new SSLSessionCache(context));
return ssf;
} catch (Exception ex) {
Log.e("ssl", "Error during the getSocketFactory");
ex.printStackTrace();
return null;
}
}
}
And when creating the queue:
sRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, ClientSSLSocketFactory.getSocketFactory()));
Hope it helps.
I believe the problem is SSL v3 based on strings like this suggesting that it is attempting to use SSL v3: SSL23_GET_SERVER_HELLO:sslv3
SSL v3 is considered insecure and thus disabled on most modern software (even in places where you would otherwise expect it to work, many companies have simply yanked it out). I also see from the stack trace that the code uses apache http client:
org.apache.http.impl.client
So you have to somehow prevent that apache http client from using ssl v3. Apache http client exists separately from the standard Java SSL/TLS libraries.
There is another stackoverflow question that received no answers:
How do disable SSLv3 in Apache HttpClient
This link looks most promising:
https://discretemkt.wordpress.com/2014/11/16/commons-httpclient-can-disable-sslv3/
In there, the key line is
Protocol.registerProtocol(scheme, customHttps);
That call appears to allow you to bypass the existing ssl factory. If you run that code first it may work, assuming the apache http client versions are compatible.
Also note that TLS 1.0 is also considered insecure these days. TLS 1.2 and 1.3 and the standard now.

How to handle invalid SSL certificates with Apache HttpClient? [duplicate]

This question already has answers here:
Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error?
(33 answers)
Closed 3 years ago.
I know, there are many different questions and so many answers about this problem... But I can't understand...
I have: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 installed "as is" from off. rep.
I need connecting to some site over the HTTPS. For this I use Apache's HttpClient.
From tutorial I read:
"Once you have JSSE correctly installed, secure HTTP communication over SSL should be as
simple as plain HTTP communication." And some example:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
By now, I write this:
HttpClient client = new HttpClient();
HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);
try {
int status = client.executeMethod(get);
System.out.println(status);
BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
int r=0;byte[] buf = new byte[10];
while((r = is.read(buf)) > 0) {
System.out.write(buf,0,r);
}
} catch(Exception ex) {
ex.printStackTrace();
}
As a result I have a set of errors:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1627)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at simpleapachehttp.Main.main(Main.java:41)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:302)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
at sun.security.validator.Validator.validate(Validator.java:235)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:191)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
... 23 more
What have I to do to create simplest SSL connection?
(Probably without KeyManager and Trust manager etc. while.)
https://mms.nw.ru uses a self-signed certificate that's not in the default trust manager set. To resolve the issue, do one of the following:
Configure SSLContext with a TrustManager that accepts any certificate (see below).
Configure SSLContext with an appropriate trust store that includes your certificate.
Add the certificate for that site to the default Java trust store.
Here's a program that creates a (mostly worthless) SSL Context that accepts any certificate:
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class SSLTest {
public static void main(String [] args) throws Exception {
// configure the SSLContext with a TrustManager
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
URL url = new URL("https://mms.nw.ru");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
System.out.println(conn.getResponseCode());
conn.disconnect();
}
private static class DefaultTrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
https://mms.nw.ru likely uses a certificate not issued by a certification authority. Consequently, you need to add the certificate to your trusted Java key store as explained in unable to find valid certification path to requested target:
When working on a client that works
with an SSL enabled server running in
https protocol, you could get error
'unable to find valid certification
path to requested target' if the
server certificate is not issued by
certification authority, but a self
signed or issued by a private CMS.
Don't panic. All you need to do is to
add the server certificate to your
trusted Java key store if your client
is written in Java. You might be
wondering how as if you can not access
the machine where the server is
installed. There is a simple program
can help you. Please download the Java
program and run
% java InstallCert _web_site_hostname_
This program opened a connection to
the specified host and started an SSL
handshake. It printed the exception
stack trace of the error that occured
and shows you the certificates used by
the server. Now it prompts you add the
certificate to your trusted KeyStore.
If you've changed your mind, enter
'q'. If you really want to add the
certificate, enter '1', or other
numbers to add other certificates,
even a CA certificate, but you usually
don't want to do that. Once you have
made your choice, the program will
display the complete certificate and
then added it to a Java KeyStore named
'jssecacerts' in the current
directory.
To use it in your program, either
configure JSSE to use it as its trust
store or copy it into your
$JAVA_HOME/jre/lib/security directory.
If you want all Java applications to
recognize the certificate as trusted
and not just JSSE, you could also
overwrite the cacerts file in that
directory.
After all that, JSSE will be able to
complete a handshake with the host,
which you can verify by running the
program again.
To get more details, you can check out
Leeland's blog No more 'unable to find
valid certification path to requested
target'
In addition to Pascal Thivent's correct answer, another way is to save the certificate from Firefox (View Certificate -> Details -> export) or openssl s_client and import it into the trust store.
You should only do this if you have a way to verify that certificate. Failing that, do it the first time you connect, it will at least give you an error if the certificate changes unexpectedly on subsequent connections.
To import it in a trust store, use:
keytool -importcert -keystore truststore.jks -file servercert.pem
By default, the default trust store should be $JAVA_HOME/jre/lib/security/cacerts and its password should be changeit, see JSSE Reference guide for details.
If you don't want to allow that certificate globally, but only for these connections, it's possible to create an SSLContext for it:
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../truststore.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
Then, you need to set it up for Apache HTTP Client 3.x by implementing one if its SecureProtocolSocketFactory to use this SSLContext. (There are examples here).
Apache HTTP Client 4.x (apart from the earliest version) has direct support for passing an SSLContext.
For Apache HttpClient 4.5+ & Java8:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial((chain, authType) -> true).build();
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContext, new String[]
{"SSLv2Hello", "SSLv3", "TLSv1","TLSv1.1", "TLSv1.2" }, null,
NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
But if your HttpClient use a ConnectionManager for seeking connection, e.g. like this:
PoolingHttpClientConnectionManager connectionManager = new
PoolingHttpClientConnectionManager();
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
The HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory) has no effect, the problem is not resolved.
Because that the HttpClient use the specified connectionManager for seeking connection and the specified connectionManager haven't register our customized SSLConnectionSocketFactory. To resolve this, should register the The customized SSLConnectionSocketFactory in the connectionManager. The correct code should like this:
PoolingHttpClientConnectionManager connectionManager = new
PoolingHttpClientConnectionManager(RegistryBuilder.
<ConnectionSocketFactory>create()
.register("http",PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory).build());
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
The Apache HttpClient 4.5 way:
org.apache.http.ssl.SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
sslContextBuilder.loadTrustMaterial(new org.apache.http.conn.ssl.TrustSelfSignedStrategy());
SSLContext sslContext = sslContextBuilder.build();
org.apache.http.conn.ssl.SSLConnectionSocketFactory sslSocketFactory =
new SSLConnectionSocketFactory(sslContext, new org.apache.http.conn.ssl.DefaultHostnameVerifier());
HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSocketFactory);
httpClient = httpClientBuilder.build();
NOTE: org.apache.http.conn.ssl.SSLContextBuilder is deprecated and org.apache.http.ssl.SSLContextBuilder is the new one (notice conn missing from the latter's package name).
From http://hc.apache.org/httpclient-3.x/sslguide.html:
Protocol.registerProtocol("https",
new Protocol("https", new MySSLSocketFactory(), 443));
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.whatever.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
Where MySSLSocketFactory example can be found here. It references a TrustManager, which you can modify to trust everything (although you must consider this!)
want to paste the answer here:
in Apache HttpClient 4.5.5
How to handle invalid SSL certificate with Apache client 4.5.5?
HttpClient httpClient = HttpClients
.custom()
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();
Once you have a Java Cert Store (by using the great InstallCert class created above), you can get java to use it by passing the "javax.net.ssl.trustStore" param at java startup.
Ex:
java -Djavax.net.ssl.trustStore=/path/to/jssecacerts MyClassName
Another issue you may run into with self signed test certs is this:
java.io.IOException: HTTPS hostname wrong: should be ...
This error occurs when you are trying to access a HTTPS url. You might have already installed the server certificate to your JRE's keystore. But this error means that the name of the server certificate does not match with the actual domain name of the server that is mentioned in the URL. This normally happens when you are using a non CA issued certificate.
This example shows how to write a HttpsURLConnection DefaultHostnameVerifier that ignore the certificates server name:
http://www.java-samples.com/showtutorial.php?tutorialid=211
EasySSLProtocolSocketFactory was giving me problems so I ended up implementing my own ProtocolSocketFactory.
First you need to register it:
Protocol.registerProtocol("https", new Protocol("https", new TrustAllSSLSocketFactory(), 443));
HttpClient client = new HttpClient();
...
Then implement ProtocolSocketFactory:
class TrustAllSSLSocketFactory implements ProtocolSocketFactory {
public static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(final X509Certificate[] certs, final String authType) {
}
public void checkServerTrusted(final X509Certificate[] certs, final String authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
private TrustManager[] getTrustManager() {
return TRUST_ALL_CERTS;
}
public Socket createSocket(final String host, final int port, final InetAddress clientHost,
final int clientPort) throws IOException {
return getSocketFactory().createSocket(host, port, clientHost, clientPort);
}
#Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress,
final int localPort, final HttpConnectionParams params) throws IOException {
return createSocket(host, port);
}
public Socket createSocket(final String host, final int port) throws IOException {
return getSocketFactory().createSocket(host, port);
}
private SocketFactory getSocketFactory() throws UnknownHostException {
TrustManager[] trustAllCerts = getTrustManager();
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, trustAllCerts, new SecureRandom());
final SSLSocketFactory socketFactory = context.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
return socketFactory;
} catch (NoSuchAlgorithmException | KeyManagementException exception) {
throw new UnknownHostException(exception.getMessage());
}
}
}
Note: This is with HttpClient 3.1 and Java 8
For a way to easily add hosts you trust at runtime without throwing out all checks, try the code here: http://code.google.com/p/self-signed-cert-trust-manager/.
I happened to face the same issue, all of a sudden all my imports were missing. I tried deleting all the contents in my .m2 folder. And trying to re-import everything , but still nothing worked.
Finally what I did was opened the website for which the IDE was complaining that it couldn't download in my browser. And saw the certificate it was using, and saw in my
$ keytool -v -list PATH_TO_JAVA_KEYSTORE
Path to my keystore was /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
that particular certificate was not there.
So all you have to do is put the certificate into the JAVA JVM keystore again.
It can be done using the below command.
$ keytool -import -alias ANY_NAME_YOU_WANT_TO_GIVE -file PATH_TO_YOUR_CERTIFICATE -keystore PATH_OF_JAVA_KEYSTORE
If it asks for password, try the default password 'changeit'
If you get permission error when running the above command.
In windows open it in administration mode.
In mac and unix use sudo.
After you have successfully added the key,
You can view it using :
$ keytool -v -list /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
You can view just the SHA-1 using teh command
$ keytool -list /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts
This link explains the requirement you have step by step. If You are not really concerned which certificate you can proceed with the process in below link.
Note You might want to double check what you are doing since, this is a unsafe operation.
Using the InstallCert to generate the jssecacerts file and do
-Djavax.net.ssl.trustStore=/path/to/jssecacerts worked great.
I'm useing httpclient 3.1.X ,and this works for me
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManager trustManager = new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{trustManager}, null);
SslContextSecureProtocolSocketFactory socketFactory = new SslContextSecureProtocolSocketFactory(sslContext,false);
Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory) socketFactory, 443));//同样会影响到HttpUtils
} catch (Throwable e) {
e.printStackTrace();
}
public class SslContextSecureProtocolSocketFactory implements SecureProtocolSocketFactory {
private SSLContext sslContext;
private boolean verifyHostname;
public SslContextSecureProtocolSocketFactory(SSLContext sslContext, boolean verifyHostname) {
this.verifyHostname = true;
this.sslContext = sslContext;
this.verifyHostname = verifyHostname;
}
public SslContextSecureProtocolSocketFactory(SSLContext sslContext) {
this(sslContext, true);
}
public SslContextSecureProtocolSocketFactory(boolean verifyHostname) {
this((SSLContext)null, verifyHostname);
}
public SslContextSecureProtocolSocketFactory() {
this((SSLContext)null, true);
}
public synchronized void setHostnameVerification(boolean verifyHostname) {
this.verifyHostname = verifyHostname;
}
public synchronized boolean getHostnameVerification() {
return this.verifyHostname;
}
public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port, clientHost, clientPort);
this.verifyHostname(sslSocket);
return sslSocket;
}
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
if(params == null) {
throw new IllegalArgumentException("Parameters may not be null");
} else {
int timeout = params.getConnectionTimeout();
Socket socket = null;
SSLSocketFactory socketfactory = this.getSslSocketFactory();
if(timeout == 0) {
socket = socketfactory.createSocket(host, port, localAddress, localPort);
} else {
socket = socketfactory.createSocket();
InetSocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
InetSocketAddress remoteaddr = new InetSocketAddress(host, port);
socket.bind(localaddr);
socket.connect(remoteaddr, timeout);
}
this.verifyHostname((SSLSocket)socket);
return socket;
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(host, port);
this.verifyHostname(sslSocket);
return sslSocket;
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
SSLSocketFactory sf = this.getSslSocketFactory();
SSLSocket sslSocket = (SSLSocket)sf.createSocket(socket, host, port, autoClose);
this.verifyHostname(sslSocket);
return sslSocket;
}
private void verifyHostname(SSLSocket socket) throws SSLPeerUnverifiedException, UnknownHostException {
synchronized(this) {
if(!this.verifyHostname) {
return;
}
}
SSLSession session = socket.getSession();
String hostname = session.getPeerHost();
try {
InetAddress.getByName(hostname);
} catch (UnknownHostException var10) {
throw new UnknownHostException("Could not resolve SSL sessions server hostname: " + hostname);
}
X509Certificate[] certs = (X509Certificate[])((X509Certificate[])session.getPeerCertificates());
if(certs != null && certs.length != 0) {
X500Principal subjectDN = certs[0].getSubjectX500Principal();
List cns = this.getCNs(subjectDN);
boolean foundHostName = false;
Iterator i$ = cns.iterator();
AntPathMatcher matcher = new AntPathMatcher();
while(i$.hasNext()) {
String cn = (String)i$.next();
if(matcher.match(cn.toLowerCase(),hostname.toLowerCase())) {
foundHostName = true;
break;
}
}
if(!foundHostName) {
throw new SSLPeerUnverifiedException("HTTPS hostname invalid: expected \'" + hostname + "\', received \'" + cns + "\'");
}
} else {
throw new SSLPeerUnverifiedException("No server certificates found!");
}
}
private List<String> getCNs(X500Principal subjectDN) {
ArrayList cns = new ArrayList();
StringTokenizer st = new StringTokenizer(subjectDN.getName(), ",");
while(st.hasMoreTokens()) {
String cnField = st.nextToken();
if(cnField.startsWith("CN=")) {
cns.add(cnField.substring(3));
}
}
return cns;
}
protected SSLSocketFactory getSslSocketFactory() {
SSLSocketFactory sslSocketFactory = null;
synchronized(this) {
if(this.sslContext != null) {
sslSocketFactory = this.sslContext.getSocketFactory();
}
}
if(sslSocketFactory == null) {
sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
}
return sslSocketFactory;
}
public synchronized void setSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
}
}
For HttpClient, we can do this :
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
String uri = new StringBuilder("url").toString();
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)
.setSSLHostnameVerifier(hostnameVerifier).build()
follow the instruction given below for Java 1.7, to create an SSL certificate using InstallCert.java program file.
https://github.com/escline/InstallCert
you must restart the tomcat
Used the following along with DefaultTrustManager and it worked in httpclient like charm. Thanks a ton!! #Kevin and every other contributor
SSLContext ctx = null;
SSLConnectionSocketFactory sslsf = null;
try {
ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
sslsf = new SSLConnectionSocketFactory(
ctx,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
} catch (Exception e) {
e.printStackTrace();
}
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();

Categories