Friday, February 05, 2010

Android: Trusting SSL certificates

We use a self-signed SSL certificate for the test version of our backend web service. Since our certificate isn't signed by a CA that Android trusts by default, we need to add our server's public certificate to our Android app's trusted store.

These same instructions apply to trusting a custom CA, except you'd get the public certificate directly from the CA instead of from a server.

Required tools:

1. Grab the public certificate from the server you want to trust. Replace ${MY_SERVER} with your server's address.

echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
 sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem

For example, here's the PEM-encoded public certificate from


2. Android has built-in support for the Bouncy Castle keystore format (BKS). Put Bouncy Castle's jar in your classpath, and create a keystore containing only your trusted key.

export CLASSPATH=bcprov-jdk16-145.jar
if [ -a $CERTSTORE ]; then
    rm $CERTSTORE || exit 1
keytool \
      -import \
      -v \
      -trustcacerts \
      -alias 0 \
      -file <(openssl x509 -in mycert.pem) \
      -keystore $CERTSTORE \
      -storetype BKS \
      -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
      -providerpath /usr/share/java/bcprov.jar \
      -storepass ez24get

3. Create a custom Apache HttpClient that uses your custom store for HTTPS connections.

import android.content.Context;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;


public class MyHttpClient extends DefaultHttpClient {

  final Context context;

  public MyHttpClient(Context context) {
    this.context = context;

  @Override protected ClientConnectionManager createClientConnectionManager() {
    SchemeRegistry registry = new SchemeRegistry();
        new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    registry.register(new Scheme("https", newSslSocketFactory(), 443));
    return new SingleClientConnManager(getParams(), registry);

  private SSLSocketFactory newSslSocketFactory() {
    try {
      KeyStore trusted = KeyStore.getInstance("BKS");
      InputStream in = context.getResources().openRawResource(R.raw.mystore);
      try {
        trusted.load(in, "ez24get".toCharArray());
      } finally {
      return new SSLSocketFactory(trusted);
    } catch (Exception e) {
      throw new AssertionError(e);

That's it! If you think this kind of stuff is fun, Square is hiring.


Blogger Ковтун said...

interface ForTestCallback {
void Finished(String result, String error);

public class ForTest extends Thread {
private ForTestCallback callBack;
private Handler handler;
private String url;

public ForTest(String adress, ForTestCallback callBack) {
this.callBack = callBack;
this.url = adress;
handler = new Handler();

public void begin() {

public void run() {
try {
final long t0 = System.currentTimeMillis();
final String result;

// fill empty keystore received a certificate
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());// BKS
store.load(null); // init empty keystore
URLConnection uc = new URL(url).openConnection();
try {
Certificate certs[] = ((HttpsURLConnection)uc).getServerCertificates();
for (int i=0; i<certs.length; i++)
store.setCertificateEntry("cert_"+i, certs[i]);
catch(Exception e) {
finally {
if (uc!=null && uc instanceof HttpURLConnection)
// check the "trusted" connection
HttpEntity responseEntity;
HttpGet httpGet = new HttpGet(url);
HttpClient client = new MyHttpClient(store);
httpGet.addHeader("Content-Type", "text/xml");
HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
HttpResponse getResponse = client.execute(httpGet);
responseEntity = getResponse.getEntity();
result = EntityUtils.toString(responseEntity);

final long t1 = System.currentTimeMillis(); Runnable() {
public void run() {
callBack.Finished(result+" for " + (t1-t0) + "ms", null);
catch (final Exception e) { Runnable() {
public void run() {
Log.d("GET_ERROR", "error", e);
callBack.Finished(null, e.getMessage());

//Your code, slightly modified ...
public class MyHttpClient extends DefaultHttpClient {
final KeyStore store;
public MyHttpClient(KeyStore store) { = store;
protected ClientConnectionManager createClientConnectionManager() {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", newSslSocketFactory(), 443));
return new SingleClientConnManager(getParams(), registry);
private SSLSocketFactory newSslSocketFactory() {
try {
return new SSLSocketFactory(store);
} catch (Exception e) {
throw new AssertionError(e);
example to call
public void callTest() {
final String url = ""; // work fine
// final String url = ""; //gets error
// final String url = ""; //gets error
ForTest test = new ForTest(url, new ForTestCallback() {
public void Finished(String result, String error) {
// show info...
startBtn.setText(bHttpsEnvSet?"get ready...":"replay...");
// next work...

11:17 AM  
Blogger Ковтун said...

I tried your code, but it did not solve my problem: (
Here is my code and your little corrected.
As noted, the connection is fine, if the server has only one certificate in the chain, otherwise we get "Not trusted server certificate"
Maybe I am wrong somewhere. I would be very grateful if you help
I deal with problems.
Thank you.

11:19 AM  
Blogger Mory said...

Works great for me. Thx a lot, that saved my day !!!

5:32 AM  
Blogger jerald said...

Hi Bob,
I am facing the following error in my android application while connecting to https server

"Not a trusted certificate"

When i used your command "echo | openssl s_client -connect ${MY_SERVER_ADDRESS}:443 2>&1 | \ sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem" to get the certificate from server, no certificate was returned and i am unable to trace where i went wrong.Kindly assist me in getting certificate from server and creating R.raw.mystore file.

10:15 AM  
Blogger Bryan said...

This post is incredibly helpful, thanks.

3:34 PM  
Blogger Jay Khimani said...

Thanks Bob. This works for me in my test environment where we have self signed certificates.

Cheers !!!
- Jay

2:28 AM  
Blogger Vittorio Pavesi said...

I'm experiencing a "NOT TRUSTED CERTIFICATE" error even if the certificate has been signed by a public CA (Thawte) and all browser works properly with it.
Have you got any suggestion ?

1:47 PM  
Blogger Philip Wrenn said...

Worked like a charm for trusting CACert CA, Thanks!

7:47 AM  
Blogger Krishna Chaitanya A said...

Thank you for this blog.

I have a similar situation where I need to generate the certificate on the device itself from my app. Is there a way to do that?
I tried using the Bouncycastle jar[bcprov-jdk16-145.jar] but the problem is that, since android already included those classes, it is not able to resolve these classes from the jar that I add. So I could not use the classes to generate certificates.

Do you know of a way to generate the certificates from my android app itself?

Thanks again.

5:33 PM  
Blogger steff said...

Hi, nice listing. But I've got a weird one. Works flawlessly for all clients over WiFi. However, it fails on some devices over 3G/GPRS with CONNECTION_TIMED_OUT or CONNECTION_REFUSED exception. Any pieces of advice?
Thanks in advance

2:59 PM  
Blogger Ranjit Iyer said...

What version of the Bouncy castle Jar was used in this example? I used the 1.6 version from but get this exception: Wrong version of key store.

7:56 PM  
Blogger Jay Khimani said...

Thanks Bob, very helpful

5:22 AM  
Blogger markww said...

The bounycastle jar is ~2mb? Am I seeing this right?

8:44 PM  
Blogger Abe said...

In your code, what does "ez24get" means?
Is that a key that I need to use with the certificate?

I continue getting the "Not trusted server certificate" error.

12:07 PM  
Blogger Bob said...

Abe, that's the password.

12:08 PM  
Blogger John Doe said...

I swear I am doing everything right, but am still getting: "Not trusted server certificate". Could there be something in the way I am executing my GET?

DefaultHttpClient httpclient = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("");

try {
HttpResponse response = httpclient.execute(get);
} catch (ClientProtocolException e) {
} catch (IOException e) {

12:24 PM  
Blogger Unknown said...

I can't get the codes to work on my machine. Kobtyh, can I have the source code for this? I am really stuck and I got tons of errors, even importing the missing classes didnt work.

3:32 AM  

Post a Comment

Links to this post:

Create a Link

<< Home