Android 7 Smack 4.1.8 "Failed to load certificate from Keystore"

Hi,

our App works fine with Smack on Android 4+, for a long time now.

Recent tests for our App with Android 7.1 resulted in “the app no longer works”.

We receive this error on A7 devices when trying to connect with Smack to our XMPP Server:

javax.net.ssl.SSLHandshakeException: java.lang.RuntimeException: Failed to load certificates from KeyStore

org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets()#1029

org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300()#956

org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run()#971

java.lang.Thread.run()#762

Do you have any hints for me, how to solve this?

Thanks in advance, Mike

This seems to be a server side issue…
are you sure your certificate is still valid. Try connecting with securitymode disabled

Thanks for your reply,

it is definitely not a server-side issue, as the same build runs fine on Android 4.x up to 6.x

Only in A7 and above this crashes.

Here is a question on another product where a user had the same issue with A7 and the author of the product fixed in his library:

Certificate issue with new Android N beta · Issue #274 · sandsmark/QuasselDroid · GitHub

stackoverflow shows similar problems:

ssl - Need help debugging SSLHandshakeException in Android Nougat - Stack Overflow

There is something to do (most likely on smack side) for a correct ssl handshake in Android 7 and above.

Show us the full stacktrace including the stacktrace of all wrapped exceptions.

ssl - Need help debugging SSLHandshakeException in Android Nougat - Stack Overflow

That is totally unrelated.

Yes, that SO article is unrelated to this specific problem, I just wanted to show, that there are changes in Android 7 regarding certificates. Problems arise in A7 with many libraries at the moment.

At the moment of the crash, this is the full stacktrace.

java.lang.Thread.run()#762 is a line in the app that does this call:

getConnection().connect();

where getConnection() has this code:

configuration = XMPPTCPConnectionConfiguration.builder()

.setUsernameAndPassword(username, password)

.setServiceName(server)

.setHost(server)

.setPort(port)

.setConnectTimeout(connectTimeoutMillis)

.setDebuggerEnabled(debuggerEnabled)

.setResource(xmppResource)

.setSecurityMode(ConnectionConfiguration.SecurityMode.required)

.setCustomSSLContext(createContext())

.build();

return new XMPPTCPConnection(configuration);

This call involves a call to createContext() — Maybe the problem is in this method, as this is the keystore related part that seems to make problems in A7

KeyStore trustStore;

try {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {

trustStore = KeyStore.getInstance(“AndroidCAStore”);

} else {

trustStore = KeyStore.getInstance(“BKS”);

}

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());

trustManagerFactory.init(trustStore);

SSLContext sslContext = SSLContext.getInstance(“TLS”);

sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());

return sslContext;

}

catch (Exception e){

Uclog.error(“Error creating SSL-context”,e);

return null;

}

In the logs the “Error creating SSL-context” does not appear, so at least this method does not fail. That’s all I know at the moment. It works fine until (and including) A6.

Thanks for your comments so far!

You still didn’t show a full stacktrace which includes all the wrapped exceptions. Also your createContext() and the according call of setCustomContext() appears to be as if you could simply remove it and would achieve the same goal.

Sorry, can you explain what you mean with “wrapped exceptions” (not native english here) - I do a e.printStacktrace and what I posted initially is the full stacktrace - there is not more.

javax.net.ssl.SSLHandshakeException: java.lang.RuntimeException: Failed to load certificates from KeyStore

org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets()#1029

There are (at least) three exception wrapped into each other. The innermost is the RuntimeException, which is wrapped into a SSLHandshakeException, which is wrapped at line 1029 of XMPPTCPConnection into a SmackException. The SmackException is propagated by the SyncPoint to the call stack of the thread invoking connect(). The stacktrace of the innermost exception is of particular interest. Possible that Android’s printStackTrace() does not a full walk of the wrapped exception (but IIRC it does). But even if not, simply attach an debugger.

I still wonder if it simply wouldn’t work if you omit the call to setCustomSSLContext(). What are you trying to achieve with the custom SSLContext anway?

Flow, thank you SO much!

By pointing out again and again you directed me the right path along this way.

I just removed the customContext and everything works.

To complete this thread, here is the full exception with all the inner exceptions, where the bold lines showed me the problem.

Connection closed with error

javax.net.ssl.SSLHandshakeException: java.lang.RuntimeException: Failed to load certificates from KeyStore

at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.ja va:361)

at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl. java:682)

at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.ja va:644)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.initReaderAndWriter(XMPPTCPConnect ion.java:659)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.proceedTLSReceived(XMPPTCPConnecti on.java:766)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$1000(XMPPTCPConnection.java :140)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPC onnection.java:1022)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPCon nection.java:956)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnecti on.java:971)

at java.lang.Thread.run(Thread.java:761)

Caused by: java.security.cert.CertificateException: java.lang.RuntimeException: Failed to load certificates from KeyStore

at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocke tImpl.java:617)

at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)

at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.ja va:357)

at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl. java:682)

at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.ja va:644)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.initReaderAndWriter(XMPPTCPConnect ion.java:659)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.proceedTLSReceived(XMPPTCPConnecti on.java:766)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$1000(XMPPTCPConnection.java :140)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPC onnection.java:1022)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPCon nection.java:956)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnecti on.java:971)

at java.lang.Thread.run(Thread.java:761)

Caused by: java.lang.RuntimeException: Failed to load certificates from KeyStore

** at android.security.net.config.KeyStoreCertificateSource.ensureInitialized(KeyStor eCertificateSource.java:70)**

at android.security.net.config.KeyStoreCertificateSource.findBySubjectAndPublicKey (KeyStoreCertificateSource.java:77)

at android.security.net.config.CertificatesEntryRef.findBySubjectAndPublicKey(Cert ificatesEntryRef.java:47)

at android.security.net.config.NetworkSecurityConfig.findTrustAnchorBySubjectAndPu blicKey(NetworkSecurityConfig.java:123)

at android.security.net.config.TrustedCertificateStoreAdapter.getTrustAnchor(Trust edCertificateStoreAdapter.java:51)

at com.android.org.conscrypt.TrustManagerImpl.findTrustAnchorBySubjectAndPublicKey (TrustManagerImpl.java:795)

at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:3 93)

at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:3 75)

at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManage rImpl.java:304)

at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(Netw orkSecurityTrustManager.java:94)

at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManage r.java:88)

at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178)

at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocke tImpl.java:596)

at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)

at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.ja va:357)

at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl. java:682)

at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.ja va:644)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.initReaderAndWriter(XMPPTCPConnect ion.java:659)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.proceedTLSReceived(XMPPTCPConnecti on.java:766)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$1000(XMPPTCPConnection.java :140)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPC onnection.java:1022)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPCon nection.java:956)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnecti on.java:971)

at java.lang.Thread.run(Thread.java:761)

Caused by: java.security.KeyStoreException: Uninitialized keystore

at java.security.KeyStore.size(KeyStore.java:1071)

at android.security.net.config.KeyStoreCertificateSource.ensureInitialized(KeyStor eCertificateSource.java:58)

at android.security.net.config.KeyStoreCertificateSource.findBySubjectAndPublicKey (KeyStoreCertificateSource.java:77)

at android.security.net.config.CertificatesEntryRef.findBySubjectAndPublicKey(Cert ificatesEntryRef.java:47)

at android.security.net.config.NetworkSecurityConfig.findTrustAnchorBySubjectAndPu blicKey(NetworkSecurityConfig.java:123)

at android.security.net.config.TrustedCertificateStoreAdapter.getTrustAnchor(Trust edCertificateStoreAdapter.java:51)

at com.android.org.conscrypt.TrustManagerImpl.findTrustAnchorBySubjectAndPublicKey (TrustManagerImpl.java:795)

at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:3 93)

at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:3 75)

at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManage rImpl.java:304)

at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(Netw orkSecurityTrustManager.java:94)

at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManage r.java:88)

at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178)

at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocke tImpl.java:596)

at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)

at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.ja va:357)

at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl. java:682)

at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.ja va:644)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.initReaderAndWriter(XMPPTCPConnect ion.java:659)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.proceedTLSReceived(XMPPTCPConnecti on.java:766)

at org.jivesoftware.smack.tcp.XMPPTCPConnection.access$1000(XMPPTCPConnection.java :140)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.parsePackets(XMPPTCPC onnection.java:1022)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader.access$300(XMPPTCPCon nection.java:956)

at org.jivesoftware.smack.tcp.XMPPTCPConnection$PacketReader$1.run(XMPPTCPConnecti on.java:971)

at java.lang.Thread.run(Thread.java:761)

I didn’t have this stacktrace from a production device and could only set up an emulator with 7.1.1 here but anyway, it worked, it was worth the effort, switching the branch, checking out the specific live version, rebuilding, setting up an emulator and and and…

So, the bug was: Up to Android 6 the KeyStore seems to check for itself, if not initialized, then doing a load or something similar. In A7 it no longer does that, it stays uninitialized.

Yes, your though was perfectly right: I just removed the custom context, and everything is fine.

To answer your question “why” I had the customContext or what I wanted to achieve with it: Honestly, making the connection have been the first steps with this library, mainly supported by code-samples from here and there (this site, stackoverflow, codeproject… the usual suspects).

It worked, and I didn’t think about it again. That’s all .

So, lets close it with a big “THANKS” in your direction!

Cheers, Mike

Honestly, making the connection have been the first steps with this library, mainly supported by code-samples from here and there (this site, stackoverflow, codeproject… the usual suspects).
I appreciate your honestly, but still have to point out: Never do cargo cult programming. Especially when it’s TLS related code.

So, the bug was: Up to Android 6 the KeyStore seems to check for itself, if not initialized, then doing a load or something similar. In A7 it no longer does that, it stays uninitialized.
I’m not sure if this analysis is correct. But happy for you if it works now.