14

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.

rjurado01
  • 5,337
  • 3
  • 36
  • 45
  • Put your certificate in `raw (By using res - right click - New Folder)`folder and access from it. – Piyush Sep 16 '16 at 10:24
  • I am developing a NativeScript application in which I have to do something like that. Like you, I have the same problem and I couldn't be able to fix it. I hope someone has an answer. – jesusgonzalezrivera Sep 16 '16 at 10:25
  • @Piyush I have updated the question with new code. – rjurado01 Sep 16 '16 at 10:50
  • 1
    What certificates are in your trust/key-store (root and intermediate certs)? What server certificates are sent by the server (leaf and intermediate certs)? – Robert Sep 16 '16 at 11:44
  • @Robert I update question with steps that I have followed to generate certificates. – rjurado01 Sep 16 '16 at 11:59
  • The client certificates are clear but what certificate is used by the server? You have to trust the server certificate - that is your problem! If the server is not using caserver.cer you do not trust the server cert. – Robert Sep 16 '16 at 12:06
  • If I make request with curl it works: `curl -v -s -k --key client.key --cert client.crt https://myurl/test.json`. – rjurado01 Sep 16 '16 at 12:59
  • did you solve it? – Choletski Oct 27 '18 at 14:25
  • Did you succeed with this issue? I am also facing the same issue (CertPathValidatorException) – Rakesh R Nair May 02 '19 at 12:50
  • https://stackoverflow.com/questions/7714993/https-connection-with-client-certificate-in-an-android-app – ChandraMouli Poreddy Apr 24 '20 at 10:36
  • It's not about the steps you used to generate the client certificate, but a problem of authenticating the server. See my answer. – auspicious99 Apr 24 '20 at 10:58

2 Answers2

0

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 !

Piyush
  • 18,895
  • 5
  • 32
  • 63
  • I change but I get this exception: System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. – rjurado01 Sep 16 '16 at 10:49
  • Are you using your server certificate or not? – Piyush Sep 16 '16 at 10:51
  • Yes of course, I update question with steps that I have followed to generate certificates. – rjurado01 Sep 16 '16 at 11:48
0

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.

auspicious99
  • 3,902
  • 1
  • 44
  • 58