Search code examples
androidsslself-signed

Using Android and a self signed certificate


I have a problem using self signed certificates with android. I face the exception java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

I will describe my context in a few sentence:

I am developing an Android Application using a REST service provided by NodeJS running on a Raspberry Pi. For the connection I want to use SSL/TLS to secure the connection, even this application will only be used in a local network.

So my problem is, I can't user Lets Encrypt for creating a cert, because there is no Domain to verify, right? My server is running local only and has no domain to verify. Therefore I created a self signed certificate for my server signed by myself. It's working fine, I added the certificate to my macOS keychain and the connection is secured. For Android I used this approach to verify my self signed certificate, but it is not working. I included the certificate of the server via a raw resource. I think this might be the problem, that I have to include the public key of the singing key into the trust manager, not the server certificate itself, right? This said I have to create a own CA? For my understanding: I create a RSA keypair and use the private key to sign certificates and my public key is needed to be included in the TrustManager, right?

I hope someone can help me :)

Edit: My method to get the SSLContext and getStringFromInputStream() is working as intended.

public static SSLContext getSslContext(Context context) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException {
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
    InputStream inputStream = context.getResources().openRawResource(R.raw.keystore);

    Certificate certificate;

    try {
        certificate = certificateFactory.generateCertificate(inputStream);
    } finally {
        inputStream.close();
    }

    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", certificate);

    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

    return sslContext;
}

AsyncTask to open a connection

public class RestRetrieveTask extends AsyncTask<String, Void, String> 
{
    private Context context;

    public RestRetrieveTask(Context context) {
        this.context = context;
    }

    @Override
    protected String doInBackground(String... strings) {
        String result = null;

        try {
            SSLContext sslContext = CustomSslCertificateAuthority.getSslContext(context);

            URL url = new URL(strings[0]);
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
            httpsURLConnection.setSSLSocketFactory(sslContext.getSocketFactory());

            InputStream in = httpsURLConnection.getInputStream();
            result = getStringFromInputStream(in);
            in.close();
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }

        return result;
    }

    @Override
    protected void onPostExecute(String s) {
        System.out.println(s);
    }
}

Solution

  • I solved this problem by creating my own CA and add the public key of the root certificate to the keystore. It's working like a charm, I just needed to create my own HostnameVerifier because the I have no hostname to verify.