Home » Android » java – Android Http request with Client Certificate

java – Android Http request with Client Certificate

Posted by: admin May 14, 2020 Leave a comment

Questions:

I’m trying to make a request to a server with a client certificate authentication with this code:

try {
    /*** CA Certificate ***/

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = getResources().openRawResource(R.raw.caserver);
    Certificate ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());

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

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

    /*** Client Certificate ***/

    KeyStore keyStore12 = KeyStore.getInstance("PKCS12");
    InputStream certInput12 = getResources().openRawResource(R.raw.p12client);
    keyStore12.load(certInput12, "123456key".toCharArray());

    // Create a KeyManager that uses our client cert
    String algorithm = KeyManagerFactory.getDefaultAlgorithm();
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
    kmf.init(keyStore12, null);


    /*** SSL Connection ***/

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

    URL url = new URL("https://myurl/test.json");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());

    System.out.println("Weeeeeeeeeee");
    InputStream in = urlConnection.getInputStream(); // this throw exception
}
catch (Exception e) {
    e.printStackTrace();
}

I obtain the next exception when the execution reach the last line InputStream in = urlConnection.getInputStream();.

System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

I have spent lots of hours trying to fix this error but I can’t find any information. When I make the same request using a web browser with the client certificate, all is ok.

Any help? Thanks in advance.

Edit

I follow this steps to generate certificates:

> openssl req -config openssl.cnf -new -x509 -extensions v3_ca -days 3650 -keyout private/caserver.key -out certs/caserver.crt
> openssl req -config openssl.cnf -new -nodes -keyout private/client.key -out client.csr -days 1095
> openssl ca -config openssl.cnf -cert certs/caserver.crt -policy policy_anything -out certs/client.crt -infiles csr/client.csr
> openssl pkcs12 -export -clcerts -in certs/client.crt  -inkey private/client.key -out p12client.p12

In my code I use caserver.crt and p12client.p12.

How to&Answers:

I don’t know why input stream unable to read certificate from Assets folder. I had the same problem. To overcome , i have put certificate in raw folder and access it through

InputStream caInput = getResources().openRawResource(R.raw.mycertificate);

and worked well !

Answer:

You appear to be focusing on the client certificate and possible problems there, but I think the error is related to the server certificate.

You have InputStream caInput = getResources().openRawResource(R.raw.caserver); which takes as input, a CA certificate of a CA that can verify that the server certificate is valid (caserver may be a DER file, for example). As your code is saying you want to trust that CA.

So, the problem may be that this file is not a correct certificate for that CA.

Or, it may really be the certificate for that CA. But that CA might not have signed your server certificate directly. Often, there is a chain of trust, where one CA might sign, then that CA is trusted by another CA, and so on, all the way up to a root CA or other CA that you trust.

So, why does the same web site, with same server certificate, work from the browser? Your browser may have a larger set of CAs that it trusts, so is able to authenticate the server. Whereas your android app may not trust one or more intermediate CAs in the chain of trust. Therefore, “Trust anchor for certification path not found.”

What can you do about it? See google’s guide on what to do with cases of unknown CA or missing intermediate CA, etc.