androidbouncycastlesamsung-mobilespongycastle

Can't initialise keystore on Samsung android


EDIT: I have now seen this error on a non-Samsung KitKat 4.4.3 device (Gigabyte/Zebra TC55).

I am trying to initialise a null KeyStore in my custom X509TrustManager class. In the constructor, I do the following:

public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException {
    super();
    //keystore.load(null);
    TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    factory.init((KeyStore) null); <--- ERROR HERE
    TrustManager[] trustmanagers = factory.getTrustManagers();
    if (trustmanagers.length == 0) {
        throw new NoSuchAlgorithmException("no trust manager found");
    }
    this.standardTrustManager = (X509TrustManager) trustmanagers[0];
}

and get the following stacktrace:

 java.security.KeyStoreException: initialization failed
    at org.spongycastle.jsse.provider.ProvTrustManagerFactorySpi.engineInit(ProvTrustManagerFactorySpi.java:127)
    at javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:191)
    at my.android.app.http.EasyX509TrustManager.<init>(EasyX509TrustManager.java:51)
    at my.android.app.http.EasySSLSocketFactory.createEasySSLContext(EasySSLSocketFactory.java:53)
    at my.android.app.http.EasySSLSocketFactory.getSSLContext(EasySSLSocketFactory.java:83)
    at my.android.app.http.EasySSLSocketFactory.createSocket(EasySSLSocketFactory.java:133)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:165)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:670)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:509)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
    at my.android.app.util.LoginUtils.loginOnline(LoginUtils.java:184)
    at my.android.app.util.LoginUtils.login(LoginUtils.java:95)
    at my.android.app.LoginActivity$UserLoginTask.doInBackground(LoginActivity.java:336)
    at my.android.app.LoginActivity$UserLoginTask.doInBackground(LoginActivity.java:320)
    at android.os.AsyncTask$2.call(AsyncTask.java:287)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
    at java.lang.Thread.run(Thread.java:856)
 Caused by: java.security.InvalidAlgorithmParameterException: trustAnchors.isEmpty()
    at java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.java:588)
    at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:84)
    at java.security.cert.PKIXBuilderParameters.<init>(PKIXBuilderParameters.java:58)
    at org.spongycastle.jsse.provider.ProvX509TrustManager.<init>(ProvX509TrustManager.java:34)
    at org.spongycastle.jsse.provider.ProvTrustManagerFactorySpi.engineInit(ProvTrustManagerFactorySpi.java:122)
    ... 23 more

I also get the same stacktrace if I try to initialise the factory as follows:

factory.init(KeyStore.getInstance(KeyStore.getDefaultType()));

As can be seen from the stack trace, I am using SpongyCastle, the Android version of BouncyCastle to handle my security/keystores etc, however, this issue is only present on Samsung devices with Android JellyBean -> KitKat. I have multiple Samsungs and it affects them all. I also have other devices running JellyBean and they are able to connect to the server just fine, so it seems its a Samsung-only issue.

Can anyone tell me how I get around this keystore init issue? I'm not using my own keystore so I want to either use a null keystore or the built-in android keystore, if possible.

Here's some more logs from logcat, from the non-Samsung 4.4.3 device, which might be helpful:

03-28 15:10:06.357 8084-8423/my.android.app W/dalvikvm: Unable to resolve superclass of Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager; (377)
03-28 15:10:06.367 8084-8423/my.android.app W/dalvikvm: Link of class 'Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager;' failed
03-28 15:10:06.367 8084-8423/my.android.app E/dalvikvm: Could not find class 'org.spongycastle.jsse.provider.ProvX509ExtendedTrustManager', referenced from method org.spongycastle.jsse.provider.ProvTrustManagerFactorySpi.engineInit
03-28 15:10:06.367 8084-8423/my.android.app W/dalvikvm: VFY: unable to resolve new-instance 3263 (Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager;) in Lorg/spongycastle/jsse/provider/ProvTrustManagerFactorySpi;
03-28 15:10:06.367 8084-8423/my.android.app D/dalvikvm: VFY: replacing opcode 0x22 at 0x006e
03-28 15:10:06.377 8084-8423/my.android.app W/dalvikvm: Unable to resolve superclass of Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager; (377)
    Link of class 'Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager;' failed
03-28 15:10:06.377 8084-8423/my.android.app E/dalvikvm: Could not find class 'org.spongycastle.jsse.provider.ProvX509ExtendedTrustManager', referenced from method org.spongycastle.jsse.provider.ProvTrustManagerFactorySpi.engineInit
03-28 15:10:06.377 8084-8423/my.android.app W/dalvikvm: VFY: unable to resolve new-instance 3263 (Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager;) in Lorg/spongycastle/jsse/provider/ProvTrustManagerFactorySpi;
03-28 15:10:06.377 8084-8423/my.android.app D/dalvikvm: VFY: replacing opcode 0x22 at 0x001e
03-28 15:10:06.387 8084-8423/my.android.app W/dalvikvm: Unable to resolve superclass of Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager; (377)
    Link of class 'Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager;' failed
03-28 15:10:06.387 8084-8423/my.android.app D/dalvikvm: DexOpt: unable to opt direct call 0x3ea1 at 0x77 in Lorg/spongycastle/jsse/provider/ProvTrustManagerFactorySpi;.engineInit
03-28 15:10:06.397 8084-8423/my.android.app W/dalvikvm: Unable to resolve superclass of Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager; (377)
    Link of class 'Lorg/spongycastle/jsse/provider/ProvX509ExtendedTrustManager;' failed
03-28 15:10:06.397 8084-8423/my.android.app D/dalvikvm: DexOpt: unable to opt direct call 0x3ea1 at 0x27 in Lorg/spongycastle/jsse/provider/ProvTrustManagerFactorySpi;.engineInit

Thanks.


Solution

  • I figured this out, so will post the answer, but I need to give away the bounty, so the first person to answer, gets it.

    As you can see from the code, for devices under Lollipop, you can't pass a null keystore to default to the system keystore, you have to get an instance by the reference AndroidCAStore. See here:

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        context = SSLContext.getInstance("TLSv1.2", new BouncyCastleJsseProvider());
        KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");//KeyStore.getDefaultType());
        keyStore.load(null, null);
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, null);
    
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
    
        context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
    } else {
        context = SSLContext.getInstance("TLSv1.2");
        context.init(null, null, null);
    }
    

    I'm not sure why this has to be done. Perhaps it's because I'm using SpongyCastle, or maybe Samsung changed something in their implementation of their SocketFactory.