Note that there are some explanatory texts on larger screens.

plurals
  1. POSSL TrustManager setup on lower Android APIs
    primarykey
    data
    text
    <p>I have an app communicating with a HTTPS RPC.<br> The HTTP server is using a CAcert signed certificate.</p> <p>I'm using a custom TrustManager for validating the certificate.</p> <ul> <li>Because I can not be sure, CAcert is included in all devices' trusted key store.</li> <li>Because I want to allow only CAcert to sign a certificate for this connection.</li> </ul> <p>However, I'm following Google's <a href="https://developer.android.com/training/articles/security-ssl.html#UnknownCa" rel="nofollow">best practices</a>. The only thing I changed is:</p> <ul> <li>Load the CAcert root certificate from a static byte[] instead a file</li> <li>Replace the last part, where the example code loads a file, with <code>HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());</code>. There is a JSONRPC2 API on top of the UrlConnection.</li> </ul> <p>Devices tested:</p> <ul> <li>working on Nexus 4 / mako running API18 / CM10.2</li> <li>working on API18 emulator</li> <li>working on API17 emulator</li> <li>working on API14 emulator</li> <li><strong>not working</strong> on a HTC G2 running API10 / CM7.* </li> <li><strong>not working</strong> on API8 emulator</li> </ul> <p>On low API devices it fails verifying the certificate during SSL handshake.<br> When trying to load <code>https://google.com</code> with this <code>TrustManager</code> on API18, it fails as expected because no trust anchor could be found.<br> So basically, this code should work and all of the methods are API1...<br> I know, that UrlConnection was broken on some lower APIs.</p> <p>How do I fix this?</p> <p>Code:</p> <pre><code>/** * Trust only CAcert's CA. CA cert is injected as byte[]. Following best practices from * https://developer.android.com/training/articles/security-ssl.html#UnknownCa */ private static void trustCAcert() { try { // Load CAs from an InputStream CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream is = new ByteArrayInputStream(CACERTROOTDER); Certificate ca; try { ca = cf.generateCertificate(is); Log.d(TAG, "ca=", ((X509Certificate) ca).getSubjectDN()); } finally { is.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 sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); HttpsURLConnection.setDefaultSSLSocketFactory( sslContext.getSocketFactory()); // added for testing only URL u = new URL( "https://myremoteapiurlsignedwiththesamecert.com/v1/doc.html"); HttpsURLConnection con = (HttpsURLConnection) u.openConnection(); con.setSSLSocketFactory(sslContext.getSocketFactory()); BufferedReader r = new BufferedReader( new InputStreamReader( con.getInputStream())); // the exception is thrown here // because verification fails String l; while ((l = r.readLine()) != null) { Log.d(TAG, "l: ", l); } } catch (IOException e) { // none of the exceptions is thrown during setup Log.e(TAG, "IOException", e); } catch (CertificateException e) { Log.e(TAG, "CertificateException", e); } catch (NoSuchAlgorithmException e) { Log.e(TAG, "NoSuchAlgorithmException", e); } catch (KeyStoreException e) { Log.e(TAG, "KeyStoreException", e); } catch (KeyManagementException e) { Log.e(TAG, "KeyManagementException", e); } } </code></pre> <p>Logs:</p> <pre><code>APIUtils D ca=OID.1.2.840.113549.1.9.1=#1612737570706F7274406361636572742E6F7267, CN=CA Cert Signing Authority, OU=http://www.cacert.org, O=Root CA E IOException E javax.net.ssl.SSLException: Not trusted server certificate E at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:371) E at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.getSecureSocket(HttpConnection.java:168) E at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:399) E at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1152) E at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:253) E at de.terminbuddy.android.network.APIUtils.trustCAcert(APIUtils.java:294) E at de.terminbuddy.android.network.APIUtils.initRpcSession(APIUtils.java:243) E at de.terminbuddy.android.network.APIUtils.runRPC(APIUtils.java:323) E at de.terminbuddy.android.network.AsyncJSONRPCTask.doInBackground(AsyncJSONRPCTask.java:55) E at de.terminbuddy.android.network.AsyncJSONRPCTask.doInBackground(AsyncJSONRPCTask.java:17) E at android.os.AsyncTask$2.call(AsyncTask.java:185) E at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) E at java.util.concurrent.FutureTask.run(FutureTask.java:137) E at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068) E at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561) E at java.lang.Thread.run(Thread.java:1096) E Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Could not validate certificate signature. E at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:168) E at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:366) E ... 15 more E Caused by: java.security.cert.CertPathValidatorException: Could not validate certificate signature. E at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:342) E at java.security.cert.CertPathValidator.validate(CertPathValidator.java:202) E at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:164) E ... 16 more E Caused by: java.security.SignatureException: Signature was not verified. E at org.apache.harmony.security.provider.cert.X509CertImpl.fastVerify(X509CertImpl.java:601) E at org.apache.harmony.security.provider.cert.X509CertImpl.verify(X509CertImpl.java:544) E at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(PKIXCertPathValidatorSpi.java:337) E ... 18 more </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload