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