We wrote client application in android which connects with https servers using HttpsUrlConnection apis. Due to Poodle vulnerability, we need to disable SSLv3 from the list of enabled protocols while invoking any request.
We followed the guidelines captured by oracle
and added following line before invoking url connection
java.lang.System.setProperty("https.protocols", "TLSv1");
This solution works fine with normal java program.
We got SSLHandShakeException when tried to connect with a server which only works on SSLv3 protocol.
But concern is : same fix does not work for android. Am I missing something or should I try another approach for android? Please suggest.
I found the solution for it by analyzing the data packets using wireshark. What I found is that while making a secure connection, android was falling back to SSLv3 from TLSv1 . It is a bug in android versions < 4.4 , and it can be solved by removing the SSLv3 protocol from Enabled Protocols list. I made a custom socketFactory class called NoSSLv3SocketFactory.java. Use this to make a socketfactory.
/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class NoSSLv3SocketFactory extends SSLSocketFactory{
private final SSLSocketFactory delegate;
public NoSSLv3SocketFactory() {
this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}
public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
private Socket makeSocketSafe(Socket socket) {
if (socket instanceof SSLSocket) {
socket = new NoSSLv3SSLSocket((SSLSocket) socket);
}
return socket;
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
}
#Override
public Socket createSocket(String host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return makeSocketSafe(delegate.createSocket(host, port));
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
}
private class NoSSLv3SSLSocket extends DelegateSSLSocket {
private NoSSLv3SSLSocket(SSLSocket delegate) {
super(delegate);
}
#Override
public void setEnabledProtocols(String[] protocols) {
if (protocols != null && protocols.length == 1 && "SSLv3".equals(protocols[0])) {
List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
if (enabledProtocols.size() > 1) {
enabledProtocols.remove("SSLv3");
System.out.println("Removed SSLv3 from enabled protocols");
} else {
System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
}
protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
}
super.setEnabledProtocols(protocols);
}
}
public class DelegateSSLSocket extends SSLSocket {
protected final SSLSocket delegate;
DelegateSSLSocket(SSLSocket delegate) {
this.delegate = delegate;
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
#Override
public String[] getEnabledCipherSuites() {
return delegate.getEnabledCipherSuites();
}
#Override
public void setEnabledCipherSuites(String[] suites) {
delegate.setEnabledCipherSuites(suites);
}
#Override
public String[] getSupportedProtocols() {
return delegate.getSupportedProtocols();
}
#Override
public String[] getEnabledProtocols() {
return delegate.getEnabledProtocols();
}
#Override
public void setEnabledProtocols(String[] protocols) {
delegate.setEnabledProtocols(protocols);
}
#Override
public SSLSession getSession() {
return delegate.getSession();
}
#Override
public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.addHandshakeCompletedListener(listener);
}
#Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
delegate.removeHandshakeCompletedListener(listener);
}
#Override
public void startHandshake() throws IOException {
delegate.startHandshake();
}
#Override
public void setUseClientMode(boolean mode) {
delegate.setUseClientMode(mode);
}
#Override
public boolean getUseClientMode() {
return delegate.getUseClientMode();
}
#Override
public void setNeedClientAuth(boolean need) {
delegate.setNeedClientAuth(need);
}
#Override
public void setWantClientAuth(boolean want) {
delegate.setWantClientAuth(want);
}
#Override
public boolean getNeedClientAuth() {
return delegate.getNeedClientAuth();
}
#Override
public boolean getWantClientAuth() {
return delegate.getWantClientAuth();
}
#Override
public void setEnableSessionCreation(boolean flag) {
delegate.setEnableSessionCreation(flag);
}
#Override
public boolean getEnableSessionCreation() {
return delegate.getEnableSessionCreation();
}
#Override
public void bind(SocketAddress localAddr) throws IOException {
delegate.bind(localAddr);
}
#Override
public synchronized void close() throws IOException {
delegate.close();
}
#Override
public void connect(SocketAddress remoteAddr) throws IOException {
delegate.connect(remoteAddr);
}
#Override
public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
delegate.connect(remoteAddr, timeout);
}
#Override
public SocketChannel getChannel() {
return delegate.getChannel();
}
#Override
public InetAddress getInetAddress() {
return delegate.getInetAddress();
}
#Override
public InputStream getInputStream() throws IOException {
return delegate.getInputStream();
}
#Override
public boolean getKeepAlive() throws SocketException {
return delegate.getKeepAlive();
}
#Override
public InetAddress getLocalAddress() {
return delegate.getLocalAddress();
}
#Override
public int getLocalPort() {
return delegate.getLocalPort();
}
#Override
public SocketAddress getLocalSocketAddress() {
return delegate.getLocalSocketAddress();
}
#Override
public boolean getOOBInline() throws SocketException {
return delegate.getOOBInline();
}
#Override
public OutputStream getOutputStream() throws IOException {
return delegate.getOutputStream();
}
#Override
public int getPort() {
return delegate.getPort();
}
#Override
public synchronized int getReceiveBufferSize() throws SocketException {
return delegate.getReceiveBufferSize();
}
#Override
public SocketAddress getRemoteSocketAddress() {
return delegate.getRemoteSocketAddress();
}
#Override
public boolean getReuseAddress() throws SocketException {
return delegate.getReuseAddress();
}
#Override
public synchronized int getSendBufferSize() throws SocketException {
return delegate.getSendBufferSize();
}
#Override
public int getSoLinger() throws SocketException {
return delegate.getSoLinger();
}
#Override
public synchronized int getSoTimeout() throws SocketException {
return delegate.getSoTimeout();
}
#Override
public boolean getTcpNoDelay() throws SocketException {
return delegate.getTcpNoDelay();
}
#Override
public int getTrafficClass() throws SocketException {
return delegate.getTrafficClass();
}
#Override
public boolean isBound() {
return delegate.isBound();
}
#Override
public boolean isClosed() {
return delegate.isClosed();
}
#Override
public boolean isConnected() {
return delegate.isConnected();
}
#Override
public boolean isInputShutdown() {
return delegate.isInputShutdown();
}
#Override
public boolean isOutputShutdown() {
return delegate.isOutputShutdown();
}
#Override
public void sendUrgentData(int value) throws IOException {
delegate.sendUrgentData(value);
}
#Override
public void setKeepAlive(boolean keepAlive) throws SocketException {
delegate.setKeepAlive(keepAlive);
}
#Override
public void setOOBInline(boolean oobinline) throws SocketException {
delegate.setOOBInline(oobinline);
}
#Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
}
#Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
delegate.setReceiveBufferSize(size);
}
#Override
public void setReuseAddress(boolean reuse) throws SocketException {
delegate.setReuseAddress(reuse);
}
#Override
public synchronized void setSendBufferSize(int size) throws SocketException {
delegate.setSendBufferSize(size);
}
#Override
public void setSoLinger(boolean on, int timeout) throws SocketException {
delegate.setSoLinger(on, timeout);
}
#Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
delegate.setSoTimeout(timeout);
}
#Override
public void setTcpNoDelay(boolean on) throws SocketException {
delegate.setTcpNoDelay(on);
}
#Override
public void setTrafficClass(int value) throws SocketException {
delegate.setTrafficClass(value);
}
#Override
public void shutdownInput() throws IOException {
delegate.shutdownInput();
}
#Override
public void shutdownOutput() throws IOException {
delegate.shutdownOutput();
}
#Override
public String toString() {
return delegate.toString();
}
#Override
public boolean equals(Object o) {
return delegate.equals(o);
}
}
}
Use this class like this while connecting :
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null,
null,
null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();
UPDATE :
Now, correct solution would be to install a newer security provider using Google Play Services:
ProviderInstaller.installIfNeeded(getApplicationContext());
This effectively gives your app access to a newer version of OpenSSL and Java Security Provider, which includes support for TLSv1.2 in SSLEngine. Once the new provider is installed, you can create an SSLEngine which supports SSLv3, TLSv1, TLSv1.1 and TLSv1.2 the usual way:
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
SSLEngine engine = sslContext.createSSLEngine();
Or you can restrict the enabled protocols using engine.setEnabledProtocols.
Don't forget to add the following dependency (latest version found here):
compile 'com.google.android.gms:play-services-auth:11.8.0'
For more info, checkout this link.
Inspired by Bhavit S. Sengar's answer, it bundled that technique into a dead simple method call. You can use the NetCipher library to get a modern TLS config when using Android's HttpsURLConnection. NetCipher configures the HttpsURLConnection instance to use the best supported TLS version, removes SSLv3 support, and configures the best suite of ciphers for that TLS version. First, add it to your build.gradle:
compile 'info.guardianproject.netcipher:netcipher:1.2'
Or you can download the netcipher-1.2.jar and include it directly in your app. Then instead of calling:
HttpURLConnection connection = (HttpURLConnection) sourceUrl.openConnection();
Call this:
HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);
At first I tried Bhavit S. Sengar's answer and it worked for most cases. But sometimes there where issues even when SSLv3 protocol was removed from Enabled Protocols on an Android 4.4.4 device. So the NetCipher library by Hans-Christoph Steiner is perfect to solve that problem as far as I could test it.
We use jsoup to make a bunch of web scraping on different servers, so we cannot set HttpsURLConnection connection = NetCipher.getHttpsURLConnection(sourceUrl);. I assume that's the same problem if you use OkHttp.
The best solution we've come to is to set the info.guardianproject.netcipher.client.TlsOnlySocketFactory from NetCipher as DefaultSSLSocketFactory in a static block. So it's set for the whole runtime of our app:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory noSSLv3Factory = new TlsOnlySocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(noSSLv3Factory);
If you like to inspect the full details (with trustAllCertificates) you can do it here.
use this code snippet, if server is SSLv3 enable then it will fail handshaking.
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("host-name", 443);
socket.setEnabledProtocols(new String[] { "TLSv1"});
socket.startHandshake();
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(null, null, null);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
httpURLConnection.setSSLSocketFactory(socketFactory);
HttpsURLConnection using TSL create a security failed, the Android implementation will fall back to SSLV3 to connection.
Please refer this http://code.google.com/p/android/issues/detail?id=78431
Using PlayService publisher client libraries running on Android I experienced the same problem when running the sample.
Fixed it with #bhavit-s-sengar's awnser above. Had to also change AndroidPublisherHelper.newTrustedTransport() to this:
SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
// NoSSLv3SocketFactory is #bhavit-s-sengar's http://stackoverflow.com/a/29946540/8524
SSLSocketFactory noSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());
NetHttpTransport.Builder netTransportBuilder = new NetHttpTransport.Builder();
netTransportBuilder.setSslSocketFactory(noSSLv3Factory);
HTTP_TRANSPORT = netTransportBuilder.build();
Connects with https server we need certificate in handshaking from client side. 1 year back I solved a similar issue using self sign certificate in the following way-
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
Usage in client side before HttpsUrlConnection
HttpsTrustManager.allowAllSSL();
hopefully it will work :)
Actually we don't need to disable the SSLV3 or TLSV1.0, What we just need to enable TLSV1.1 or TLSv1.2 in android < 5 devices.
The problem is TLSv1.1 and TLSv1.2 not enabled on Android <5 by default and to connect using these latest secure protocol we must have to enable in Android <5 devices.
This solution fixed my problem : https://stackoverflow.com/a/45853669/3448003
Related
i had a code which app can access https, just like this :
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
chain[0].checkValidity();
} catch (Exception e) {
throw new CertificateException("Certificate not valid or trusted.");
}
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
after that, google play console give me a notice :
Apache Trust Manager
Your app is using an unsafe implementation of the X509TrustManager interface with an Apache HTTP client, resulting in a security vulnerability. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.
Insecure Hostname Verifier
Your app is using an unsafe implementation of HostnameVerifier. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.
is there something wrong with my code ??
I'm getting the following error while trying to run on devices below android lollipop and it is working really well for version above lollipop.
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x943e670: 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:658 0xb750d3a1:0x00000000)
this is my method to register the user:
String tag_string_req = "register";
StringRequest strReq = new StringRequest(Request.Method.POST,
AppConfig.URL_REGISTER, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, "Register Response: " + response.toString());
hideDialog();
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
if (!error) {
String id = jObj.getString("id");
finish();
} else {
String errorMsg = jObj.getString("error_msg");
Toast.makeText(getApplicationContext(),
errorMsg, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Registration Error: " + error.getMessage());
Toast.makeText(getApplicationContext(),
error.getMessage(), Toast.LENGTH_LONG).show();
hideDialog();
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("user_name", txtSEditName.getText().toString().trim());
params.put("password", txtSEditPhone.getText().toString().trim());
return params;
}
};
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
My volley singleton looks like this:
public class AppController extends Application {
public static final String TAG = AppController.class.getSimpleName();
private RequestQueue mRequestQueue;
private static AppController mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized AppController getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
and my app config file looks like this:
public static String URL_REGISTER = "http://192.168.56.1/MyApp/register.php";
Can anyone help me solve this issue?
Error: javax.net.ssl.SSLHandshakeException:
javax.net.ssl.SSLProtocolException: SSL handshake aborted:
I have tried to sort out the error and this has worked for me. What I have understood was that for devices below that of lollipop the protocols TLSv1.1 and TLSv1.2 are not enabled by default. Inorder to enable them for devices such using jellybean or kitkat we will have to use a SSLSocketFactory.
So now I have made the following change to the getRequestQueue() method of Volley singleton:
public RequestQueue getRequestQueue() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
Log.d("msg", "HI, I m a kitkat phone");
try {
ProviderInstaller.installIfNeeded(getApplicationContext());
} catch (GooglePlayServicesRepairableException e) {
// Indicates that Google Play services is out of date, disabled, etc.
// Prompt the user to install/update/enable Google Play services.
GooglePlayServicesUtil.showErrorNotification(e.getConnectionStatusCode(), getApplicationContext());
e.printStackTrace();
} catch (GooglePlayServicesNotAvailableException e) {
// Indicates a non-recoverable error; the ProviderInstaller is not able
// to install an up-to-date Provider.
e.printStackTrace();
}
HttpStack stack = null;
try {
stack = new HurlStack(null, new TLSSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
stack = new HurlStack();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
Log.d("Your Wrapper Class", "Could not create new stack for TLS v1.2");
stack = new HurlStack();
}
mRequestQueue = Volley.newRequestQueue(getApplicationContext(), stack);
} else {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
And create a class named TLSSocketFactory.java and add the following code:
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
#Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
#Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
One more step is to add the following implementation in your dependencies of gradle file:
implementation 'com.google.android.gms:play-services-base:11.0.0'
I hope this might help you to resolve this issue.
may be I can help somebody.
in my case the problem was from the webserver. I tried to change the webserver and used Github as a test and every thing went fine.
so, my advise is to check and confirm first the website support required SSLxxxx.
My question is similar to: SSLSocketFactory in java
I need to set a custom SSLSocketFactory...except I do NOT have an https connection (it's LDAPS), so can't use:
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
...to set the SSLSocketFactory. I have an SSLContext object initialized but when I make the LDAP connection the default SSLContext is called automatically since my custom one is not set:
dirContext = new InitialDirContext(env); // <-- reverts to default ssl context
Is there a non-HTTPS equivalent method to line #3 below:
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(myKeyManagerFactory.getKeyManagers(), myTrustManagerArray, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
Yes, there is.
env.put("java.naming.ldap.factory.socket", UnsecuredSSLSocketFactory.class.getName());
UnsecuredSSLSocketFactory.java:
public class UnsecuredSSLSocketFactory extends SSLSocketFactory
{
private SSLSocketFactory socketFactory;
public UnsecuredSSLSocketFactory()
{
try
{
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager()
{
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string){}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string){}
#Override
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}}, new SecureRandom());
socketFactory = sslContext.getSocketFactory();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
#SuppressWarnings("unused")
public static SocketFactory getDefault()
{
return new UnsecuredSSLSocketFactory();
}
#Override
public String[] getDefaultCipherSuites()
{
return socketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites()
{
return socketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException
{
return socketFactory.createSocket(socket, string, i, bln);
}
#Override
public Socket createSocket(String string, int i) throws IOException
{
return socketFactory.createSocket(string, i);
}
#Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException
{
return socketFactory.createSocket(string, i, ia, i1);
}
#Override
public Socket createSocket(InetAddress ia, int i) throws IOException
{
return socketFactory.createSocket(ia, i);
}
#Override
public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException
{
return socketFactory.createSocket(ia, i, ia1, i1);
}
#Override
public Socket createSocket() throws IOException
{
return socketFactory.createSocket();
}
}
Note, if the issue is just a hostname mismatch (which is super common in clustered Active Directory Environments), you can just set the system property com.sun.jndi.ldap.object.disableEndpointIdentification to true, so as a command line arg -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
Note this will only ignore a hostname mismatch on the certificate, you will still need to have a trust chain from ldap's cert to something in your truststore, but this seems to be the most common issue people have with SSL, LDAP and Active Directory, as the certificate's the domain generate for each domain controller don't include a subject alternate name for the domain itself, so if you follow the standard example of just pointing ldap to yourcomapanydomain.com, when it resolves to domaincontroller1.yourcompanydomain.com you get a failure. Note, if you are upgrading from an old java version, this behavior changed in https://www.oracle.com/java/technologies/javase/8u181-relnotes.html
My Android app tells me that my https certificate doesn't match the hostname:
javax.net.ssl.SSLException: hostname in certificate didn't match: <hostname1> != <oldhostname>
What is odd is that
The website (hostname1) gives the correct certificate (checked with browsers and the ssllabs tool)
oldhostname is the previous hostname I had set up in previous versions of the app
Is there some kind of cache for certificates? I cant't find any info on that
Add this class
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context != null ? context.getSocketFactory() : null);
}
}
and call it from your MainActivity with HttpsTrustManager.allowAllSSL();
Although it's not save approach but i solve my problem with this.
I am currently working on a client/server TLS tool that requires us to connect through firewalls.
For reasons that are outside our control, we are only granted an outgoing TCP connection.
The problem is that our client's firewall blocks the client Hello v2 message (and possibly the whole SSL handshake).
Is there a way to obfuscate the stream in some manner?
I was thinking about trying to use compression to make the stream unreadable to the firewall.
(Maybe using JDK7's GzipOutputStream which now allows for syncFlush flushing)
I am no SSL expert but it seems to me it should be possible to translate the whole stream which should make it impossible for the firewall to pick up the connection and block it.
As far as I can see, there are a few (two?) ways to go about this :
Override the default implementation
Implement SSLServerSocketFactory
The first option didn't work out for me as I am unable to find the source code of com.sun.net.ssl.internal.ssl.SSLServerSocketFactoryImpl, which is the default implementation.
I did browse the openJDK source code for it, but even there, the sources appear to be missing.
Implementing a SSLServerSocketFactory is beyond my capabilities. As I said I am no SSL expert.
Please note that the application does work fine through other, less agressive firewalls / firewall rules.
Compressing an encrypted stream is not useful, where you actually only want some masking to avoid your firewall.
On the client side, you can use the SSLSocketFactory's method createSocket(socket, host, port, autoclose) to create a SSL socket based on another socket - and this another socket can get your special SocketImpl implementation, doing a simple XOR-masking on the first some bytes.
On the server side, it is more complicated, since the SSLServerSocketFactory has no such method.
In a answer to Java RMI + SSL + Compression = IMPOSSIBLE!, I described how to build a delegating Socket factory. There it was done for a Rmi(Client|Server)SocketFactory, but it would work in an analogous way for a ServerSocketFactory or SocketFactory.
But of course it could be that your firewall is not actually blocking SSL traffic, but blocking anything that is not whitelisted (like HTTP). Before building your wrapping socket implementation, try if a simple socket+serversocket which sends some random data and receives them back even works.
It is possible to tunnel TCP through HTTP without additional development. There are various tools. Look at GNU httptunnel. httptunnel creates a bidirectional virtual data connection tunnelled in HTTP requests. The HTTP requests can be sent via an HTTP proxy if so desired.Httpc is quite interresting too.
Perhaps slightly off-topic, but if the problem is specifically about SSLv2, and if SSLv3 or TLS 1.x handshakes work fine, you could disable the V2 ClientHello, by using SSLSocket.setEnabledProtocols(new String[] { "SSLv3", "TLSv1", "TLSv1.1" }).
See the JSSE Reference guide (section on SSLContext).
EDIT: For those who don't read comments, here is a link to #EJP's answer with more details on this topic: Why does Java's SSLSocket send a version 2 client hello?
It appears the solution is to combine Bruno's suggestion and Paulo's solution.
Paulo's solution allows us to customize the behavior of our SSLSocket or SSLServerSocket using delegates.
Bruno's suggestion allows us to tell the default SSL implementation to use our modified SSLSocket or SSLServerSocket.
Here is what I did :
Create a delegate ServerSocket class ( MyServerSocket )
Create a delegate ServerSocketFactory class (MyServerSocketFactory)
Create a delegate SocketFactory class (MySocketFactory)
Create a delegate Socket class (MySocket)
Create XorInputStream (find it here)
Create XorOutputStream (find it here)
On the server side :
// Initialisation as usual
...
sslSocketFactory = sslContext.getSocketFactory();
serverSocketFactory = ServerSocketFactory.getDefault();
serverSocketFactory = new MyServerSocketFactory(serverSocketFactory);
serverSocket = serverSocketFactory.createServerSocket(port);
...
Socket s = (Socket) serverSocket.accept();
sslSocket = (SSLSocket) sslSocketFactory.createSocket(s, null, s.getPort(), false);
sslSocket.setUseClientMode(false);
sslSocket.setEnabledCipherSuites(new String[]{"SSL_RSA_WITH_RC4_128_MD5"});
sslSocket.setNeedClientAuth(true);
...
On the client side:
Socket s = new MySocketFactory(SocketFactory.getDefault()).createSocket(host, port);
SSLSocket socket = (SSLSocket) factory.createSocket(s, host, port, false);
Sources
public class MyServerSocket extends ServerSocket {
private ServerSocket baseSocket;
public MyServerSocket(ServerSocket baseSocket) throws IOException {
this.baseSocket = baseSocket;
}
#Override
public Socket accept() throws IOException {
return new MySocket(baseSocket.accept());
}
#Override
public void bind(SocketAddress endpoint) throws IOException {
baseSocket.bind(endpoint);
}
#Override
public void bind(SocketAddress endpoint, int backlog) throws IOException {
baseSocket.bind(endpoint, backlog);
}
#Override
public void close() throws IOException {
baseSocket.close();
}
#Override
public ServerSocketChannel getChannel() {
return baseSocket.getChannel();
}
#Override
public InetAddress getInetAddress() {
return baseSocket.getInetAddress();
}
#Override
public int getLocalPort() {
return baseSocket.getLocalPort();
}
#Override
public SocketAddress getLocalSocketAddress() {
return baseSocket.getLocalSocketAddress();
}
#Override
public synchronized int getReceiveBufferSize() throws SocketException {
return baseSocket.getReceiveBufferSize();
}
#Override
public boolean getReuseAddress() throws SocketException {
return baseSocket.getReuseAddress();
}
#Override
public synchronized int getSoTimeout() throws IOException {
return baseSocket.getSoTimeout();
}
#Override
public boolean isBound() {
return baseSocket.isBound();
}
#Override
public boolean isClosed() {
return baseSocket.isClosed();
}
#Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
baseSocket.setPerformancePreferences(connectionTime, latency, bandwidth);
}
#Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
baseSocket.setReceiveBufferSize(size);
}
#Override
public void setReuseAddress(boolean on) throws SocketException {
baseSocket.setReuseAddress(on);
}
#Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
baseSocket.setSoTimeout(timeout);
}
#Override
public String toString() {
return baseSocket.toString();
}
}
public class MyServerSocketFactory extends ServerSocketFactory {
private ServerSocketFactory baseFactory;
public MyServerSocketFactory(ServerSocketFactory baseFactory) {
this.baseFactory = baseFactory;
}
#Override
public ServerSocket createServerSocket(int i) throws IOException {
return new MyServerSocket(baseFactory.createServerSocket(i));
}
#Override
public ServerSocket createServerSocket(int i, int i1) throws IOException {
return new MyServerSocket(baseFactory.createServerSocket(i, i1));
}
#Override
public ServerSocket createServerSocket(int i, int i1, InetAddress ia) throws IOException {
return new MyServerSocket(baseFactory.createServerSocket(i, i1, ia));
}
}
public class MySocket extends Socket {
private Socket baseSocket;
public MySocket(Socket baseSocket) {
this.baseSocket = baseSocket;
}
private XorInputStream xorInputStream = null;
private XorOutputStream xorOutputStream = null;
private final byte pattern = (byte)0xAC;
#Override
public InputStream getInputStream() throws IOException {
if (xorInputStream == null)
{
xorInputStream = new XorInputStream(baseSocket.getInputStream(), pattern);
}
return xorInputStream;
}
#Override
public OutputStream getOutputStream() throws IOException {
if (xorOutputStream == null)
{
xorOutputStream = new XorOutputStream(baseSocket.getOutputStream(), pattern);
}
return xorOutputStream;
}
#Override
public void bind(SocketAddress bindpoint) throws IOException {
baseSocket.bind(bindpoint);
}
#Override
public synchronized void close() throws IOException {
baseSocket.close();
}
#Override
public void connect(SocketAddress endpoint) throws IOException {
baseSocket.connect(endpoint);
}
#Override
public void connect(SocketAddress endpoint, int timeout) throws IOException {
baseSocket.connect(endpoint, timeout);
}
#Override
public SocketChannel getChannel() {
return baseSocket.getChannel();
}
#Override
public InetAddress getInetAddress() {
return baseSocket.getInetAddress();
}
#Override
public boolean getKeepAlive() throws SocketException {
return baseSocket.getKeepAlive();
}
#Override
public InetAddress getLocalAddress() {
return baseSocket.getLocalAddress();
}
#Override
public int getLocalPort() {
return baseSocket.getLocalPort();
}
#Override
public SocketAddress getLocalSocketAddress() {
return baseSocket.getLocalSocketAddress();
}
#Override
public boolean getOOBInline() throws SocketException {
return baseSocket.getOOBInline();
}
#Override
public int getPort() {
return baseSocket.getPort();
}
#Override
public synchronized int getReceiveBufferSize() throws SocketException {
return baseSocket.getReceiveBufferSize();
}
#Override
public SocketAddress getRemoteSocketAddress() {
return baseSocket.getRemoteSocketAddress();
}
#Override
public boolean getReuseAddress() throws SocketException {
return baseSocket.getReuseAddress();
}
#Override
public synchronized int getSendBufferSize() throws SocketException {
return baseSocket.getSendBufferSize();
}
#Override
public int getSoLinger() throws SocketException {
return baseSocket.getSoLinger();
}
#Override
public synchronized int getSoTimeout() throws SocketException {
return baseSocket.getSoTimeout();
}
#Override
public boolean getTcpNoDelay() throws SocketException {
return baseSocket.getTcpNoDelay();
}
#Override
public int getTrafficClass() throws SocketException {
return baseSocket.getTrafficClass();
}
#Override
public boolean isBound() {
return baseSocket.isBound();
}
#Override
public boolean isClosed() {
return baseSocket.isClosed();
}
#Override
public boolean isConnected() {
return baseSocket.isConnected();
}
#Override
public boolean isInputShutdown() {
return baseSocket.isInputShutdown();
}
#Override
public boolean isOutputShutdown() {
return baseSocket.isOutputShutdown();
}
#Override
public void sendUrgentData(int data) throws IOException {
baseSocket.sendUrgentData(data);
}
#Override
public void setKeepAlive(boolean on) throws SocketException {
baseSocket.setKeepAlive(on);
}
#Override
public void setOOBInline(boolean on) throws SocketException {
baseSocket.setOOBInline(on);
}
#Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
baseSocket.setPerformancePreferences(connectionTime, latency, bandwidth);
}
#Override
public synchronized void setReceiveBufferSize(int size) throws SocketException {
baseSocket.setReceiveBufferSize(size);
}
#Override
public void setReuseAddress(boolean on) throws SocketException {
baseSocket.setReuseAddress(on);
}
#Override
public synchronized void setSendBufferSize(int size) throws SocketException {
baseSocket.setSendBufferSize(size);
}
#Override
public void setSoLinger(boolean on, int linger) throws SocketException {
baseSocket.setSoLinger(on, linger);
}
#Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
baseSocket.setSoTimeout(timeout);
}
#Override
public void setTcpNoDelay(boolean on) throws SocketException {
baseSocket.setTcpNoDelay(on);
}
#Override
public void setTrafficClass(int tc) throws SocketException {
baseSocket.setTrafficClass(tc);
}
#Override
public void shutdownInput() throws IOException {
baseSocket.shutdownInput();
}
#Override
public void shutdownOutput() throws IOException {
baseSocket.shutdownOutput();
}
#Override
public String toString() {
return baseSocket.toString();
}
}
public class MySocketFactory extends SocketFactory {
private SocketFactory baseFactory;
public MySocketFactory(SocketFactory baseFactory) {
this.baseFactory = baseFactory;
}
#Override
public Socket createSocket() throws IOException {
return baseFactory.createSocket();
}
#Override
public boolean equals(Object obj) {
return baseFactory.equals(obj);
}
#Override
public int hashCode() {
return baseFactory.hashCode();
}
#Override
public String toString() {
return baseFactory.toString();
}
#Override
public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
return new MySocket(baseFactory.createSocket(string, i));
}
#Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
return baseFactory.createSocket(string, i, ia, i1);
}
#Override
public Socket createSocket(InetAddress ia, int i) throws IOException {
return baseFactory.createSocket(ia, i);
}
#Override
public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
return baseFactory.createSocket(ia, i, ia1, i1);
}
}