Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trust Only Particular Certificate Issued by CA - Android

Tags:

android

ssl

ca

I am developing an Android application which requires SSL handshaking to be done only if the server has a particular certificate issued by a CA(For eg: GoDaddy). I referred the documentation on Android developer website but it only says about verifying a self signed certificate or certificate that is not trusted by Android.In my case should I get the client certificate and add it to my keystore.I am using apache HttpClient for my webservice requests. Any help is much appreciated.

like image 321
androidGuy Avatar asked Feb 18 '14 06:02

androidGuy


People also ask

Where does Android store CA certificates?

Android stores CA certificates in its Java keystore in /system/etc/security/cacerts.

How do I trust a CA certificate?

Expand Policies > Windows Settings > Security Settings > Public Key Policies. Right-click Trusted Root Certification Authorities and select Import. Click Next and Browse to select the CA certificate you copied to the device. Click Finish and then OK.


2 Answers

It is actually simple. You have to override the checkServerTrusted in your X509TrustManager and throw a CertificateException if the issuer is not GoDaddy. In the code I provided, I used "bla bla", you should probably get the exact name.

You have first to use the provider for your Http Requests: This provider will be used to do the requestes using provider.execute function:

private static AbstractHttpClient provider;
static {

    try {
        BasicHttpParams httpParameters = new BasicHttpParams();
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
        ClientConnectionManager ccm = new ThreadSafeClientConnManager(httpParameters, registry);
        provider = new DefaultHttpClient(ccm, httpParameters);
    } catch (Exception e) {
        e.printStackTrace();
        provider = new DefaultHttpClient();
    }
}

Now you need your EasySSLSocketFactory:

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

Finally, and here is the work, you need the EasyX509TrustManager which will not accept except certificates issued by GoDaddy:

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 
    {  
        X509Certificate c = certificates[0];
        String name = c.getIssuerDN().getName();
        if(!"bla bla".equals(name))
            throw new CertificateException("OMG! it is not bla bla!");
        standardTrustManager.checkServerTrusted(certificates, authType);    
    }  

    /** 
     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() 
     */  
    public X509Certificate[] getAcceptedIssuers() 
    {  
        return this.standardTrustManager.getAcceptedIssuers();  
    }    
}  
like image 134
Sherif elKhatib Avatar answered Nov 04 '22 21:11

Sherif elKhatib


You need to

  1. Add the peer certificate to your truststore, so you trust it.
  2. Remove the CA certificate from your truststore, so you don't trust any other certificates he has signed.
like image 34
user207421 Avatar answered Nov 04 '22 20:11

user207421