Android and self-signed ssl certificates

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!

Kommentare

  1. Hey, thanks a lot! I was googling for this issue without luck. It works like a charm.
    Thanks,
    Sebastian

  2. This is exactly what I was looking for and it worked great.
    Nice Job.

  3. Hi, you saved my live. I searched one week for a solution...only found non working code snippets.
    Thanks

  4. Does this work with proxy. I am behind proxy and it gets me timed out

  5. Thanks a lot!

  6. 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.

  7. Thanks for the hint, we'll look into it!

  8. 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];
    }
    }

  9. 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.

  10. 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

  11. thank you so much for this!

  12. 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!

  13. Sorry I really need to do this but i have to clue where to start, can anybody help me out.

  14. 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.

  15. Seems like the server doesn't support ssl on the port you are using.

  16. 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.

  17. Thank you! That is a life saver!

  18. 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 :)

  19. 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.

  20. Awesome job. This is exactly what I needed.

  21. 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

  22. 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.

  23. I was looking for this solution form past 3 weeks...
    U rock..

  24. 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...

  25. 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.

  26. 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.

  27. Hello I want to use the class Duex call for the httpclient how

  28. Hello I want to use the two-class s call for the httpclient how I will cope

  29. 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

  30. 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...

  31. 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.

  32. Tobias, can you please add some code for client and for server side - how to use those snippets. Unfortunately it is not clear.

  33. Thank you a lot!!
    This is a great code!!

  34. 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.

  35. @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.

  36. 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...

  37. @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.

  38. 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.

  39. 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.

  40. @Tobias can we use the same for mail server authentication, what changes are needed for using the same. thanks.

  41. @Mukunda Have a look at this one http://stackoverflow.com/questions/5297770/android-javamail-api-imap-over-ssl/5731655#5731655

  42. 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

  43. @ambrish It shouldn't make a difference with weblogic... what error do you get?

  44. 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?

  45. @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.

  46. Is it possible for you to show me how I would incorporate this code into send a JSON object?

  47. Just use a HttpPost Object instead of the HttpGet and set a StringEntity with your JSON data to the HttpPost with setEntity().

  48. 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.

  49. 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

  50. Yup, you don't need any special SSLSocketFactory and TrustManager then. Just doing without them should work.

  51. 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.

  52. 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.

  53. I tried many ways but error still exists: not trusted server certificate. That troubles me a lot.

  54. 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

  55. 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.

  56. @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

  57. 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!?

  58. Hi Tobias,
    is SSL v3 working on Android version 2.3 or later.
    Thanks.
    Pushpe

  59. 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 ;-)

  60. 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.

  61. 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

  62. Great exaples, thanks for the help!

  63. Hi all,
    could you please tell how to do same thing with ksoap2 library of android to call a webservie over https

  64. Thank you for this code :) ...

  65. 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.