My application uses WebViewClient to make SSL connections to the server.
The server is configured to only accept TLSv1.1 and above protocols.
How do I check which SSL protocols are a) Supported and b) Enabled by default when using Android WebViewClient on a device.
How do I enable specific SSL protocols for Android WebViewClient instance used in my application.
On one of the test devices running Android 4.3,
WebViewClient throws onReceivedError callback with the following description:
"Failed to perform SSL handshake"
Chrome logs are as follows:
01-29 15:58:00.073 5486 5525 W chromium_net: external/chromium/net/http/http_stream_factory_impl_job.cc:865: [0129/155800:WARNING:http_stream_factory_impl_job.cc(865)] Falling back to SSLv3 because host is TLS intolerant: 10.209.126.125:443 01-29 15:58:00.083 5486 5525 E chromium_net: external/chromium/net/socket/ssl_client_socket_openssl.cc:792: [0129/155800:ERROR:ssl_client_socket_openssl.cc(792)] handshake failed; returned 0, SSL error code 5, net_error -107
My application also uses HttpClient and HttpsUrlConnection classes to setup SSL Connections. I was able to use SSLSocket API to enable specific protocols when using these classes.
http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String[])
I need to do the same with WebViewClient.
As per documenation it is NOT possible to support TLS 1.0 in WebView in Android < 4.3. For Android 4.4 it is disabled by default.
Check this chart for support of TLS 1.0 in different browsers: https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers
If your app is using, or you are willing to use, Google Play services, you can use newer security features on older phones by installing their Provider. It is easy to install, only one line (plus exception handling, etc). You will also need to add google play services to your gradle file if you do not already have it. ProviderInstaller is included in the -base package.
try {
ProviderInstaller.installIfNeeded(this);
} catch (GooglePlayServicesRepairableException e) {
// Fix it
} catch (GooglePlayServicesNotAvailableException e) {
// Skip it
}
For a full example, see "Updating Your Security Provider to Protect Against SSL Exploits" from Google.
Actually, I managed to make it work, but you need okHttp library for that.
Try this when you're setting up browser activity:
WebViewClient client = new WebViewClient() {
private OkHttpClient okHttp = new OkHttpClient.Builder().build();
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Request okHttpRequest = new Request.Builder().url(url).build();
try {
Response response = okHttp.newCall(okHttpRequest).execute();
return new WebResourceResponse(response.header("Content-Type", "plain/text"), response.header("Content-Encoding", "deflate"), response.body().byteStream());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
webView.setWebViewClient(client);
Also, you'll need classic Trust Manager Manipulator, SSL socket factory and its implementation in your Application class:
public class TrustManagerManipulator implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] acceptedIssuers = new X509Certificate[] {};
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
public static void allowAllSSL()
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[] { new TrustManagerManipulator() };
}
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());
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return acceptedIssuers;
}
}
SSl Socket Factory:
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
TrustManager[] managers = new TrustManager[] { new TrustManagerManipulator() };
context.init(null, managers, new SecureRandom());
internalSSLSocketFactory = context.getSocketFactory();
}
#Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
#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;
}
}
App class:
public class App extends Application {
private static App appInstance;
#Override
public void onCreate() {
super.onCreate();
setupSSLconnections();
}
private void setupSSLconnections() {
try {
HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
it's beause android 4.3 not support TSL 1.1 but only TSL1.0
read this article
https://www.ssllabs.com/ssltest/clients.html
find android 4.3 will see
Protocols
TLS 1.3 No
TLS 1.2 No
TLS 1.1 No
TLS 1.0 Yes
SSL 3 INSECURE Yes
SSL 2 No
Related
I am trying to connect server using https url But still could not understand how should I add SSL certificate to jersey client
private static String post() throws Exception {
String url ="https://www.test.lk/services/erl/es/erl/view/index.action";
Client client =createClient();
try {
return client
.target(url)
.request()
.get()
.readEntity(String.class);
} finally {
client.close();
}
}
private static Client createClient() throws Exception {
SSLContext ctx = SSLContext.getInstance("SL");
ctx.init(null, getTrustManager(), new SecureRandom());
HostnameVerifier verifier = new HostnameVerifier() {
#Override
public boolean verify(String hostName, SSLSession sslSession) {
return true;
}
};
return ClientBuilder.newBuilder().sslContext(ctx).hostnameVerifier(verifier).build();
}
private static TrustManager[] getTrustManager() {
return new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Trust all servers
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Trust all clients
}
} };
}
I found the solution. I just need to add certificate to the java KeyStore This helped me
I am in the process of replacing Apache Httpclient 3.1 to 4.5 version, Our application is using AXIS 2 SOAP Web Service stub which underneath is using HTTPClient 3.1 TransportSender. I need to migrate it to use HttpClient 4.5 version. Below is the complete code that need to be migrated to HttpClient 4.5 version :
import org.apache.commons.httpclient.protocol.Protocol; // 3.1 version
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; // 3.1 version
final Options clientOptions = stub._getServiceClient().getOptions();
clientOptions.setProperty(HTTPConstants.CUSTOM_PROTOCOL_HANDLER, new Protocol("https", new TLSSocketFactory(), 443));
public class TLSSocketFactory extends SSLSocketFactory implements SecureProtocolSocketFactory {
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(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(String s, int i, InetAddress inetAddress, int i1, HttpConnectionParams httpConnectionParams) throws IOException, UnknownHostException, ConnectTimeoutException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, i, inetAddress, i1));
}
#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;
}
}
I came across this StackOverflow post : How to configure SSL with Axis2 using httpClient4
But its clearly mentioned in the post that it is only compatible upto httpclient 4.4.1.
Axis2 1.7.0 supports Apache HttpClient 4.x in addition to the no
longer maintained Commons HttpClient 3.x. To enable the support for
HttpClient 4.x, use
org.apache.axis2.transport.http.impl.httpclient4.HTTPClient4TransportSender
instead of org.apache.axis2.transport.http.CommonsHTTPTransportSender
in axis2.xml. Please note that the code was written for HttpClient
4.2.x and should work with 4.3.x and 4.4.x, but is incompatible with 4.5.x.
We are using HttpClient 4.5 and it clearly says it's incompatible with 4.5.x
I am really stuck and need help on migrating the above piece of code to use HttpClient 4.5.
Thanks in advance.
The steps mentioned in the below SO Post works for HttpClient 4.5.x as well.
configure SSL with Axis2 using httpClient4
Trying to access https service using below client which has certificates.
This work fine with Java7 when used in Java8 its throwing socket connection exception.
Should any other certificates need to be added ?
protected Client getClient() {
if (client == null) {
try {
TrustManager[] trustCertificates = new TrustManager[] { new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} };
final SSLContext sslContext = SSLContext.getInstance(SSL);
sslContext.init(null, trustCertificates, new java.security.SecureRandom());
final HostnameVerifier verifiedHosts = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
ClientBuilder clientBuilder = ClientBuilder.newBuilder();
ClientConfig config = new ClientConfig();
config.register(JacksonJsonProvider.class);
config.register(Log4j2JerseyLoggingFilter.class);
return clientBuilder.sslContext(sslContext).hostnameVerifier(verifiedHosts).withConfig(config).build();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
LOG.error("Client Builder Exception " + e);
}
}
return null;
}
Getting socket connection exception.
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:189)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.readV3Record(InputRecord.java:593)
at sun.security.ssl.InputRecord.read(InputRecord.java:529)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1371)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1355)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1281)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1256)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
at org.glassfish.jersey.client.internal.HttpUrlConnector$4.getOutputStream(HttpUrlConnector.java:385)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:194)
Java 7 doesn't use TLSv1.2 by default but Java 8 does. The fact it worked in Java 7 points to the server not supporting TLSv1.2. Perhaps you need to change to whatever the server requires, along the lines of:
final SSLContext sslContext = SSLContext.getInstance("TLSv1");
I have a project in which HttpsURLConnection is configed to use a customized TrustManager as following:
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{new MyTrustManager()}, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
There is a REST API client in this project, it uses Jersey client to send HTTP/HTTPS request:
Client client = ClientBuilder.newClient();
However, the HTTPS connection initiated by this Jerset client does not use the defaultSSLSocketFactory I set in HttpsURLConnection and it fails to connect to untrusted HTTPS url.
I need to explicitly set the SslContext on this client to make it work with my TrustManager.
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{new MyTrustManager()}, null);
Client client = ClientBuilder.newBuilder().sslContext(sslContext).build();
Is there any way to solve this issue?
Thanks.
The solution I eventually found is to set the SSLSocketFactory provider property to a customized SSLSocketFactory. Hope this can help others who have similar issues.
Call this in beginning of the program:
Security.setProperty("ssl.SocketFactory.provider", MySSLSocketFactory.class.getCanonicalName());
Here is how MySSLSocketFactory looks like (it also sets connection timeout):
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLContext sslContext = SSLContext.getInstance(Const.Ssl.PROTOCOL_SSL);
public MySSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
this.sslContext.init(
null,
new TrustManager[] { new MyTrustManager(false) },
new SecureRandom());
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException {
socket.connect(new InetSocketAddress(host, port), Const.Ssl.CONNECT_TIMEOUT);
socket.setSoTimeout(Const.Ssl.DATA_TIMEOUT);
return this.sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
#Override
public String[] getDefaultCipherSuites() {
return this.sslContext.getSocketFactory().getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return this.sslContext.getSocketFactory().getSupportedCipherSuites();
}
#Override
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException {
return this.createSocket(new Socket(), host, port, true);
}
#Override
public Socket createSocket(InetAddress address, int port)
throws IOException {
return this.createSocket(new Socket(), address.getHostAddress(), port, true);
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return this.createSocket(new Socket(), host, port, true);
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return this.createSocket(new Socket(), address.getHostAddress(), port, true);
}
Since Android 5.0.2 or 5.1.1 on Samsung devices my Android app receive error message when connecting web interface of old router "FRITZ!Box 7170".
javax.net.ssl.SSLProtocolException: SSL handshake aborted:
ssl=0xaecc7e00: Failure in SSL library, usually a protocol error
error:14082174:SSL routines:SSL3_CHECK_CERT_AND_ALGORITHM:got Channel
ID before a ccs (external/openssl/ssl/s3_clnt.c:3632
0xaf0e1679:0x00000000)
If I connect same interface with Firefox Browser:
ssl_error_weak_server_ephemeral_dh_key
I think it is because of unsafe Diffie-Hellman key length?
How to avoid this? I am using HTTPClient to make the connection.
I have a same problem.
The reason was a samsung security update, which has change a default cipher suite array given by SSLSocketFactory. Apropos if you take a nexus device with android M on it, you will see this error message
ssl_error_weak_server_ephemeral_dh_key
On samsung devices it is a
SSL3_CHECK_CERT_AND_ALGORITHM
The solution for me was to override cipher suites array. Here is my SSLSocketFactory, which I use to create ssl sockets.
public class SpeedportSSLSocketFactory extends SSLSocketFactory {
private final static Logger logger = Logger.getLogger(SpeedportSSLSocketFactory.class);
/**
* the order of ciphers in this list is important here e.g. TLS_DHE_* must not stay above TLS_RSA_*
*/
private static final String[] APPROVED_CIPHER_SUITES = new String[]{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
};
private SSLSocketFactory factory;
public SpeedportSSLSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{
// accepts certs with valid but expired key chain (incl. root cert)
new ExpiredSpeedportTrustManager()}, new java.security.SecureRandom());
factory = sslcontext.getSocketFactory();
} catch (Exception ex) {
logger.error("Cannot create SpeedportSSLSocketFactory", ex);
}
}
// dirty
private void injectHostname(InetAddress address, String host) {
try {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(address, host);
} catch (Exception ignored) {
logger.error("Cannot inject hostName");
}
}
public static SocketFactory getDefault() {
return new SpeedportSSLSocketFactory();
}
public Socket createSocket() throws IOException {
return factory.createSocket();
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return factory.createSocket(socket, host, port, autoClose);
}
public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException {
return factory.createSocket(addr, port, localAddr, localPort);
}
public Socket createSocket(InetAddress inaddr, int i) throws IOException {
return factory.createSocket(inaddr, i);
}
public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) throws IOException {
return factory.createSocket(host, port, localAddr, localPort);
}
public Socket createSocket(String host, int port) throws IOException {
InetAddress addr = InetAddress.getByName(host);
injectHostname(addr, host);
Socket socket = factory.createSocket(addr, port);
((SSLSocket) socket).setEnabledCipherSuites(getSupportedCipherSuites());
return socket;
}
#Override
public String[] getDefaultCipherSuites() {
return APPROVED_CIPHER_SUITES;
}
#Override
public String[] getSupportedCipherSuites() {
return APPROVED_CIPHER_SUITES;
}
}
Last two methods override default cipher suite. I am not sure, what you need override both.
The order in cipher suites array is also very important