Posted on by | Posted in Mobile Blog, Tutorial | Tagged , , , , ,

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!



68 Responses to Android and self-signed ssl certificates

← Older Comments
  1. Nirmal says:

    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


  2. Tobias Knell Tobias Knell says:

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


  3. Florin Filip says:

    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.


  4. Tobias Knell Tobias Knell says:

    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.


  5. april says:

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


  6. Nizaqat says:

    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


  7. Krzysztof says:

    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.


  8. Tobias Knell Tobias Knell says:

    @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


  9. Erik says:

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


  10. pushpe says:

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


  11. Sabrina H. says:

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


  12. Tobias Knell Tobias Knell says:

    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.


  13. Michael says:

    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


  14. Oscar says:

    Great exaples, thanks for the help!


  15. Pingback: Could not validate certificate signature? | BlogoSfera

  16. Nitin says:

    Hi all,

    could you please tell how to do same thing with ksoap2 library of android to call a webservie over https


  17. Buksy says:

    Thank you for this code :)


  18. Sandro Souza says:

    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.


← Older Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>