Asaf Nevo
Asaf Nevo

Reputation: 11688

Android How to allow specific SSL certificate from a specific server

I'm trying to connect to my secured server using HTTPS using DefaultHttpClient.

My server is using PositiveSSL certificate and every check i've made through the SSL checkers online - gives an A grade for the certificate.

Apperantly, PositiveSSL is not a verified CA Root in Android so when i'm ever i'm trying to connect to my server I'm receiving the javax.net.ssl.SSLPeerUnverifiedException: No peer certificate which is discussed here in various questions.

One of the given solution (that was the best for me) is to create a custom SSLFactory and to allow it to accept the unverified certificate, in case this certificate is coming from my server (to set the allowed server's name).

I couldn't find any code examples on how to do it.. would be great if someone will be kind to help me with that

Upvotes: 1

Views: 3906

Answers (2)

Piyush Agarwal
Piyush Agarwal

Reputation: 25858

Yopu can Write you Custom SSLSocketFactory as given below

 public class MySSLSocketFactory extends SSLSocketFactory {
 SSLContext sslContext = SSLContext.getInstance("TLS");

 public MySSLSocketFactory(KeyStore truststore)
        throws NoSuchAlgorithmException, KeyManagementException,
        KeyStoreException, UnrecoverableKeyException {
    super(truststore);

    TrustManager tm = 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 null;
        }
    };

    sslContext.init(null, new TrustManager[]{tm}, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port,
                           boolean autoClose) throws IOException, UnknownHostException {
    return sslContext.getSocketFactory().createSocket(socket, host, port,
            autoClose);
}

@Override
public Socket createSocket() throws IOException {
    return sslContext.getSocketFactory().createSocket();
}
 }

Now Create a Custom HttpClient using the SocketFactory to by Pass the certificate. You can also accept some particular Certificate as well using your SockeFactory.

   public static DefaultHttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);
        SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);

    } catch (UnrecoverableKeyException e) {
        return new DefaultHttpClient();
    } catch (NoSuchAlgorithmException e) {
        return new DefaultHttpClient();
    } catch (CertificateException e) {
        return new DefaultHttpClient();
    } catch (IOException e) {
        return new DefaultHttpClient();
    } catch (KeyStoreException e) {
        return new DefaultHttpClient();
    } catch (KeyManagementException e) {
        return new DefaultHttpClient();
    }
}

Now Use instantiate httpclient as mentioned :

DefaultHttpClient client = getNewHttpClient();

Check emmby Answer on this post

Trusting all certificates using HttpClient over HTTPS

UPDATED :

If you want to verify only some particular server. Open the url in browser with Https and download the certificate from the url bar , Its easy Google it how to do that. It will give you a certificate file.

Copy that certificate in your assest folder. and do the following.

  private X509Certificate getCertFromFile(String path) throws Exception {
     AssetManager assetManager = MyActivity.this.getResources().getAssets();
     InputStream inputStream = null;
     try {
         inputStream = assetManager.open(path);
     } catch (IOException e) {
         e.printStackTrace();
     }
     InputStream caInput = new BufferedInputStream(inputStream);
     X509Certificate cert = null;
     CertificateFactory cf = CertificateFactory.getInstance("X509");
     cert = (X509Certificate) cf.generateCertificate(caInput);
     cert.getSerialNumber();
     return cert;
  }

Now in above code for socket Factory return this certificate instead of null.

 @Override
    public X509Certificate[] getAcceptedIssuers() {
        X509Certificate[] certificates=new X509Certificate[]{getCertFromFile(PATH_OF_CERTIFICATE_FILE)};

       return certificates;
         //asset folder path is  taken in method.Only provide path respective to asset folder like /certificate/MyCertificate.crt inside assest.

    }

Upvotes: 1

Robert
Robert

Reputation: 42710

You can place the custom server certificate in a custom trust store (BKS file), include it in your app and initialize your SSLSocketFactory with it:

SSLSocketFactory sslSocketFactory = new SSLSocketFactory(trustStore);

Afterwards use that SSLSocketFactory for creating your connections.

In such a case only connections to servers which certificate is included in the key store.

A very good example how this can be used with HttpClient can be found on Nikolay Elenkov's home page (section Using your own trust store: HttpClient):

http://nelenkov.blogspot.de/2011/12/using-custom-certificate-trust-store-on.html

Upvotes: 0

Related Questions