Android and self-signed ssl certificates
24.06.2010
Tobias Knell
Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let android accept them.
But fortunately, there’s a workaround that uses an own SSLSocketFactory and an own TrustManager. With this, only your added site is beeing able to be called, so theres no security issue.
First you have to create the SSLFactory:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.scheme.LayeredSocketFactory; import org.apache.http.conn.scheme.SocketFactory; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; /** * This socket factory will create ssl socket that accepts self signed certificate * * @author olamy * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse $ * @since 1.2.3 */ public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory { private SSLContext sslcontext = null; private static SSLContext createEasySSLContext() throws IOException { try { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null); return context; } catch (Exception e) { throw new IOException(e.getMessage()); } } private SSLContext getSSLContext() throws IOException { if (this.sslcontext == null) { this.sslcontext = createEasySSLContext(); } return this.sslcontext; } /** * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int, * java.net.InetAddress, int, org.apache.http.params.HttpParams) */ public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException { int connTimeout = HttpConnectionParams.getConnectionTimeout(params); int soTimeout = HttpConnectionParams.getSoTimeout(params); InetSocketAddress remoteAddress = new InetSocketAddress(host, port); SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket()); if ((localAddress != null) || (localPort > 0)) { // we need to bind explicitly if (localPort < 0) { localPort = 0; // indicates "any" } InetSocketAddress isa = new InetSocketAddress(localAddress, localPort); sslsock.bind(isa); } sslsock.connect(remoteAddress, connTimeout); sslsock.setSoTimeout(soTimeout); return sslsock; } /** * @see org.apache.http.conn.scheme.SocketFactory#createSocket() */ public Socket createSocket() throws IOException { return getSSLContext().getSocketFactory().createSocket(); } /** * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket) */ public boolean isSecure(Socket socket) throws IllegalArgumentException { return true; } /** * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int, * boolean) */ public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); } // ------------------------------------------------------------------- // javadoc in org.apache.http.conn.scheme.SocketFactory says : // Both Object.equals() and Object.hashCode() must be overridden // for the correct operation of some connection managers // ------------------------------------------------------------------- public boolean equals(Object obj) { return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class)); } public int hashCode() { return EasySSLSocketFactory.class.hashCode(); } }
And the TrustManager:
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** * @author olamy * @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $ * @since 1.2.3 */ public class EasyX509TrustManager implements X509TrustManager { private X509TrustManager standardTrustManager = null; /** * Constructor for EasyX509TrustManager. */ public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException { super(); TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); factory.init(keystore); TrustManager[] trustmanagers = factory.getTrustManagers(); if (trustmanagers.length == 0) { throw new NoSuchAlgorithmException("no trust manager found"); } this.standardTrustManager = (X509TrustManager) trustmanagers[0]; } /** * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType) */ public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException { standardTrustManager.checkClientTrusted(certificates, authType); } /** * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType) */ public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException { if ((certificates != null) && (certificates.length == 1)) { certificates[0].checkValidity(); } else { standardTrustManager.checkServerTrusted(certificates, authType); } } /** * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() */ public X509Certificate[] getAcceptedIssuers() { return this.standardTrustManager.getAcceptedIssuers(); } }
(Both classes are from exchangeit with a small change on the EasySSLSocketFactory to work on android 2.2)
Now we have to do some other preparations and create a HttpClient that we can use to establish the connection:
//members private ClientConnectionManager clientConnectionManager; private HttpContext context; private HttpParams params;
//constructor public WebService(){ setup(); } //prepare for the https connection //call this in the constructor of the class that does the connection if //it's used multiple times private void setup(){ SchemeRegistry schemeRegistry = new SchemeRegistry(); // http scheme schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); // https scheme schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443)); params = new BasicHttpParams(); params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1); params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1)); params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, "utf8"); CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); //set the user credentials for our site "example.com" credentialsProvider.setCredentials(new AuthScope("example.com", AuthScope.ANY_PORT), new UsernamePasswordCredentials("UserNameHere", "UserPasswordHere")); clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry); context = new BasicHttpContext(); context.setAttribute("http.auth.credentials-provider", credentialsProvider); }
public HttpResponse getResponseFromUrl(String url){ //connection (client has to be created for every new connection) client = new DefaultHttpClient(clientConnectionManager, params); HttpGet get = new HttpGet(url); HttpResponse response = client.execute(get, context); return response; }
And that's it. I hope this will help some of you to solve their problems with self-signed certs!
Sandro Souza
Thank you very much Tobias.
These source codes are GOLD for me. :D
My friend, may you tell us if there's a way (like your examples) for use https in WebViews?
I need to use a WebView to load pages from my site using https.
Thank you in advance.
Buksy
Thank you for this code :) ...
Nitin
Hi all,
could you please tell how to do same thing with ksoap2 library of android to call a webservie over https
Oscar
Great exaples, thanks for the help!
Michael
Hi Tobi,
I've searched a long time for a solution to create a https connection, but I don't found one. This post was very very helpful. Thanks a lot!
best regards
Tobias Knell
Hi Sabrina,
sorry for the late answer, did you find a solution yet? I did not reuse the http client objects before, and I don't have the time right now to go and try it. Doesn't it work with HTTPS connections just like with the HTTP connections before?
I think I tried to reuse the client once, but it didn't work out, so I dropped the thought... but I'm not sure anymore.
Sabrina H.
Hi Tobi,
thank you really much for your efforts and this nice work.
before I found your site, I used
http://foo.jasonhudgins.com/2009/08/http-connection-reuse-in-android.html
for my http connections, that allowed me to reuse the http client objects.
Main reason for this was, that I was able to use the same Cookiestore over different activities.
Is it much work to do HTTPS Connection Reuse with your work?
As I am fairly new to Android and HTTP things, I wonder how this could be accoplished??
liebe Grüße aus dem nahen Süden ;-)
pushpe
Hi Tobias,
is SSL v3 working on Android version 2.3 or later.
Thanks.
Pushpe
Erik
Im kind of new to SSL
This worked right away very smooth.
I did many tutorials where they but BKS files in the res/raw folder, like clienttruststore.bks. Im confused now because you dont mention them!?
Tobias Knell
@april: do you use the classes mentioned in the blogpost?
@Nizaqat: Sorry, I don't have a answer for that one. Can you connect to twitter with the browser on your phone?
@Krzysztof: I haven't looked into this myself, but maybe this might help you: http://stackoverflow.com/questions/6769665/reusing-an-http-connection-in-android
Krzysztof
Hi
I`m fighting 5th day on problem with persistent https connection. Have you any working demo? I have my own certs (for client as well, both sides), trustStore and keyStore works fine with SSLContex nad httpsUrlConnection. But how to handle connection reuse (tcpdumpo on server show FIN packets every single time)? HttpUrlConnetion reuse connections, HttpsUrlConnection not. I`m freaking put! Thx in advance.
Nizaqat
public class WebService
{
//members
private ClientConnectionManager clientConnectionManager;
private HttpContext context;
private HttpParams params;
//constructor
public WebService(){
setup();
}
// prepare for the https connection
//call this in the constructor of the class that does the connection if
//it's used multiple times
private void setup(){
SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
// schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// https scheme
schemeRegistry.register(new Scheme("https", new SSLFactory(), 443));
params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf8");
/*
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//set the user credentials for our site "example.com"
credentialsProvider.setCredentials(new AuthScope("example.com", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("UserNameHere", "UserPasswordHere"));
*/
clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);
context = new BasicHttpContext();
//context.setAttribute("http.auth.credentials-provider", credentialsProvider);
}
public HttpResponse getResponseFromUrl(String url){
//connection (client has to be created for every new connection)
DefaultHttpClient client = new DefaultHttpClient(clientConnectionManager, params);
HttpGet get = new HttpGet("https://twitter.com/#!/UTZCERTIFIED");
HttpResponse response = null;
try {
response = client.execute(get, context);
Log.i("Response:",""+response.toString());
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
I am facing Exception: java.net.UnknownHostException: twitter.com
Please guide me. Thanks
april
I tried many ways but error still exists: not trusted server certificate. That troubles me a lot.
Tobias Knell
Hello Florin,
you have to create new classes for the SSLFactory and the TrustManager and copy them into these classes. In the constructor of your class that does the HTTPS connections, you call the setup() method (in which you put the right ports and credentials before) and in another method in this class you create the HttpClient and place a get/post/whatever with it.
Florin Filip
Hi Tobias,
Could you please shed me some light?
I am tring to use an application on my android phone and I get the error message: 'Network Error: No peer certificate'. I do use an own self signed ssl certficate which has been already installed on the phone. I also enabled use secure credentials option on my settings phone. It is still not working.
I did not manage to understand how should I use an own SSLSocketFactory.
Could you please provide a step by step how to?
Where should I copy the SSLFactory, TrustManager, HttpClient etc?
Thanks in advance.
Nirmal
It works like a charm. To take this to production with a Valid CA Certificate, then should i need to comment out this code?
Thanks
Tobias Knell
Yup, you don't need any special SSLSocketFactory and TrustManager then. Just doing without them should work.
Sarah
Mostly just posting this for the reference of others. Thanks for your excellent code, I adapted it into a custom SSLSocketFactory for a JDBC SSL connection to my postgresql database in Android. I used spongycastle to manage the keys.
Thomas
Is it possible for you to show me how I would incorporate this code into send a JSON object?
Tobias Knell
Just use a HttpPost Object instead of the HttpGet and set a StringEntity with your JSON data to the HttpPost with setEntity().
Donal
I'm just looking into this at the minute, with this solution do I have to use the bouncy castle jar and pre-sign certificates using the Java KeyTool?
Tobias Knell
@Donal You don't have to use the bounce castle jar, android comes with everything you need for this. Yes, you need to sign your certificate by using the Java KeyTool.
ambrish
i am facing same problem, but the thing is i am using weblogic 8.1 SP5. so can anyone help me to fix the same problem by using weblogic
Tobias Knell
@ambrish It shouldn't make a difference with weblogic... what error do you get?
Mukunda
@Tobias can we use the same for mail server authentication, what changes are needed for using the same. thanks.
Tobias Knell
@Mukunda Have a look at this one http://stackoverflow.com/questions/5297770/android-javamail-api-imap-over-ssl/5731655#5731655
Don
Hi Tobias,
Is it possible to see a simple but complete example using the above ?
I'm new to Java / Android and there's a huge learning curve but self signed certs accessing a remote mysql database is something I'd like to do.
Tobias Knell
Hi Don,
I updated the post a bit to make it a bit easier to understand. You only need to replace the string values and call the methods from some other class.
Erik
Hi, great post. I'm trying to get it to work with this site: "secure.quickpay.dk:443" but it won't succeed.
Can anyone make it work with that site?
Tried a lot of different approaches, both trying to allow all hostnames and to put the certificate in my keystore...
Tobias Knell
@Erik what errors do occur on the different approaches? Maybe you'll start by searching what they mean and probably you'll find a solution for it.
Tobias Knell
@AlexZ: There is no server-side code for this example and for the client side this is all the code you need to do a connection over https to a server with a self signed ssl certificate.
@Jeremy: You can simply copy&paste it right from the page, no need to view the source. The whole code is in <pre> tags and the page is utf-8 encoded, so there shouldn't be a problem on our side.
Jeremy
Thank you, thank you, thank you. I spent at least 3 hours trying a half-dozen different variants on this solution from StackOverflow, all of which failed in different ways. This worked immediately. Thank you so much.
One suggestion: make the code available for direct download. I grabbed it by doing view-source, then copy-and-paste, but there were a couple of places where the escaping of "&&" caused an error.
Fernando
Thank you a lot!!
This is a great code!!
AlexZ
Tobias, can you please add some code for client and for server side - how to use those snippets. Unfortunately it is not clear.
Ben
So if I read the comment above correctly the Credentials section can be omitted if my server doesn't ask for credentials right?
Also, how does this only accept certificates from my site? I don't see anywhere to specify which site is "mine". If it's not clear, I mean that I would expect to have to change something in your code to "www.mysite.com" to indicate that certificates from www.mysite.com" are acceptable or to embed the server's certificate in the code somewhere...
Tobias Knell
You should only need the credentials if your server asks for them. (I Haven't tried it without them, though)
To check the certificate, I would try the checkServerTrusted method in the EasyTrustManager. You get the certificate from the server there, and can check if it is yours.
AndroidRIM
bonjour je veux une activité qui va utiliser httpclient mais je sais pas comment je vais appeler les deux classes désoler je ne comprend pas l anglais
AndroidRIM
Hello I want to use the two-class s call for the httpclient how I will cope
AndroidRIM
Hello I want to use the class Duex call for the httpclient how
lbm
I keep getting this error no matter how I try to connect to HTTPS:
05-26 01:02:03.364: ERROR/Erro(1218): org.bouncycastle.jce.exception.ExtCertPathValidatorException: Could not validate certificate signature.
Tobias Knell
The only thing I can think of right now is, that you haven't set up your server certificate properly. Maybe check it again/create a new one. If that isn't the problem, I don't know what could cause the error.
shivdattam
I am calling koap web service from android emulator....
as far as the web service is hosted on private ip and accessed as HTTP then i can access but i can not access that as HTTPS..getting SSL Handshake Exception...
any help is extremely appreciated......
thanks and regards...
Basha
I was looking for this solution form past 3 weeks...
U rock..
macuserlambda
hi,
I tried this code. For a request without authentication it works well. But I need to authenticate in an other function and the username and password are not sent... The server doesn't get username and password fields so it returns an authentication failed error.
Any idea ?
Thanks in advance
Tobias Knell
I use this code with basic auth on the server side and it works fine - apperantly for everyone else as well. So I assume the problem lies within your server / webapp, because the username and password are sent for sure.
Bryan
Awesome job. This is exactly what I needed.
GTX
Thanks a lot.
this works great.
What is the purpose of the username and password in the code? I left them untouched, but the code still works :)
Tobias Knell
The username and password are only needed if you configure an authentication on your server, so they work fine as they are, if you haven't.
Sandeep Asokan
Thank you! That is a life saver!
Nancy
Thanks for the reply.
I tried to open other web site (https sites), but still I get the same error.
Pasting the customized line:
credentialsProvider.setCredentials(new AuthScope("my url", 443),new UsernamePasswordCredentials("my user name", "my pwd"));
HttpGet get = new HttpGet("my https url");
Can you please help me solve this, i'm struck with this for a very long time.
It will be helpful, if you would share a working example.
Thanks.
nancy
I am using your code for my work,but its not working for the android emulator. I am getting below error
ERROR/OpenSSLSocketImpl(714): Unknown error 1 during connect
SSL handshake failure: Failure in SSL library, usually a protocol error
error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol (external/openssl/ssl/s23_clnt.c:585 0xaf586674:0x00000000)
Do I need to make any changes so that it works in the emulator.can you help me with your suggestions.
Tobias Knell
Seems like the server doesn't support ssl on the port you are using.
Josuf
Sorry I really need to do this but i have to clue where to start, can anybody help me out.
Jeff
Like all previous comments, I very much appreciate that you took the time to publish this *working* code. I wanted to issue JSON-RPC web service requests from an android app to my personal server but wanted an encrypted solution. Along with others here I have my own self-signed certificate.
This worked a champ for me. thumbs-up!
Don
thank you so much for this!
Ricardo
Thank You so much!
I was struggling to find a way to make HTTPS requests
and like the ones above me, none of the code that I found really worked.
You're the Man!
Thanks 8-D
JJ
I'm curious... what did you change to make it work on Android 2.2?
And this is the EasyX509TrustManager class I'm using (see below). Is your class a better choice?
class EasyX509TrustManager implements 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];
}
}
Tobias Knell
For 2.2 the return value of the method
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
needed to be changed from
return getSSLContext().getSocketFactory().createSocket();
to
return getSSLContext().getSocketFactory().createSocket(socket, , host, port, autoClose);
As far as I can see, your TrustManager excepts ssl certificates from anywhere, doesn't it?
The one that we use only excepts certificates from sites that you specify in your app to prevent some safety issues.
Fabrizio
In my try this solutions doesn't works with Android 2.2, I get always "java.io.IOException: SSL handshake failure: I/O error during system call, Unknown error: 0". In previous version (I've tested 2.1 and 1.6) it works fine.
David Linsin
Thanks for the hint, we'll look into it!
Samy
Thanks a lot!
samar
Does this work with proxy. I am behind proxy and it gets me timed out
Stefan
Hi, you saved my live. I searched one week for a solution...only found non working code snippets.
Thanks
Jude
This is exactly what I was looking for and it worked great.
Nice Job.
Sebastián Treu
Hey, thanks a lot! I was googling for this issue without luck. It works like a charm.
Thanks,
Sebastian