Search code examples
androidssltomcat7

Android SSL - CertPathValidatorException


My HTTPS service is hosted in TomCat (.keystore created with the help of keytool in JDK bin).

Server certificate is self signed & made it as trusted by installing it in Trusted Root Authorities .

I am able to access the service from browser, but unable to do the same from Android. I am a beginner in J2EE.

enter image description here

host.cert (placed in assets dir) is created with OpenSSL.

I am using the code snippet from Android Developers site

        try {
            // Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt

            InputStream inn=getAssets().open("host.cert");

            Certificate ca;
            try {
                ca = cf.generateCertificate(inn);
                Log.i("TEST","ca=" + ((X509Certificate) ca).getSubjectDN());
            } finally {
                inn.close();
            }

// Create a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);

// Create an SSLContext that uses our TrustManager
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);

// Tell the URLConnection to use a SocketFactory from our SSLContext
            URL url = new URL("https://192.168.56.1:8443/RestHTTPS/JavaCodeGeeks/AuthorService/authors/");
            HttpsURLConnection urlConnection =
                    (HttpsURLConnection)url.openConnection();
            urlConnection.setSSLSocketFactory(context.getSocketFactory());
            InputStream in = urlConnection.getInputStream();

            byte arr[]=new byte[in.available()];
            in.read(arr);

            String str=new String(arr);
            Log.i("TEST",str);

        }catch (Exception e){
            Log.e("TEST",e.toString());
            e.printStackTrace();
        }

I know this is already asked question, but I am unable find a solution.


Solution

  • I will post the solution , for somebody still facing the issue.

    Mainly note 3 things (This is already stated in Android developer site)

    • Create custom Trust Manager to bypass default Trust Manager.
    • Manually verify that the host name is an acceptable.
    • Never use "localhost" or "127.0.0.1" for testing purposes , instead use your PC's IP address.

    Here is the complete code

    try {
    
                // Things to Note 1 : Bypass default Trust Managers
                TrustManager[] byPassTrustManagers = new TrustManager[]{new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
    
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }
    
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }
                }};
    
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                InputStream inn = getAssets().open("keystore.cer");
                Certificate ca;
                try {
                    ca = cf.generateCertificate(inn);
                    Log.i("TEST", "ca=" + ((X509Certificate) ca).getSubjectDN());
                } finally {
                    inn.close();
                }
    
    
                String keyStoreType = "BKS";
                KeyStore.getDefaultType();
                KeyStore keyStore = KeyStore.getInstance(keyStoreType);
                keyStore.load(null, null);
                keyStore.setCertificateEntry("ca", ca);
    
                String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
                TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
                tmf.init(keyStore);
    
                SSLContext context = SSLContext.getInstance("TLS");
                context.init(null, byPassTrustManagers, null);
    
                // Things to Note 2 : Don't use "localhost" ,instead use IP
                URL url = new URL("https://192.168.56.1:8443/RestHTTPS/JavaCodeGeeks/AuthorService/authors");
                HttpsURLConnection urlConnection =
                        (HttpsURLConnection) url.openConnection();
    
                urlConnection.setSSLSocketFactory(context.getSocketFactory());
    
                // Things to Note 3 : Allow all host
                urlConnection.setHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
    
                Log.i("TEST", urlConnection.getResponseMessage() + "");
    
                InputStream in = urlConnection.getInputStream();
    
                InputStreamReader isw = new InputStreamReader(in);
    
                int data = isw.read();
                String str = "";
                while (data != -1) {
                    char current = (char) data;
                    data = isw.read();
                    str += current;
                }
    
                Log.i("TEST", "" + str);
    
            } catch (Exception e) {
                Log.e("TEST", e.toString());
                e.printStackTrace();
            }