diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index f64c91580..0c320bafd 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -2,7 +2,6 @@ package com.fsck.k9; import java.io.File; -import java.security.PrivateKey; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -1435,14 +1434,4 @@ public class K9 extends Application { editor.commit(); } } - - /** - * Holding a reference to PrivateKey selected for client certificate - * authentication. We need to keep reference to this key so it won't get - * garbage collected. If it will then the whole app will crash - * on Android <= 4.2 with "Fatal signal 11 code=1". - * - * see https://code.google.com/p/android/issues/detail?id=62319 - */ - public static ArrayList sClientCertificateReferenceWorkaround = new ArrayList(); } diff --git a/src/com/fsck/k9/security/KeyChainKeyManager.java b/src/com/fsck/k9/security/KeyChainKeyManager.java index 6511398c0..79a45723d 100644 --- a/src/com/fsck/k9/security/KeyChainKeyManager.java +++ b/src/com/fsck/k9/security/KeyChainKeyManager.java @@ -27,6 +27,8 @@ import com.fsck.k9.mail.ClientCertificateRequiredException; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class KeyChainKeyManager extends X509ExtendedKeyManager { + private static PrivateKey sClientCertificateReferenceWorkaround; + private String mAlias; public KeyChainKeyManager() { @@ -87,20 +89,24 @@ public class KeyChainKeyManager extends X509ExtendedKeyManager { if (K9.DEBUG) Log.d(K9.LOG_TAG, "KeyChainKeyManager.getPrivateKey for " + alias); - PrivateKey key = KeyChain.getPrivateKey(K9.app, alias); + PrivateKey key; + + /* + * We need to keep reference to the first private key retrieved so + * it won't get garbage collected. If it will then the whole app + * will crash on Android < 4.2 with "Fatal signal 11 code=1". See + * https://code.google.com/p/android/issues/detail?id=62319 + */ + if (sClientCertificateReferenceWorkaround == null + && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { + key = retrieveFirstPrivateKey(alias); + } else { + key = KeyChain.getPrivateKey(K9.app, alias); + } if (key == null) { throw new IllegalStateException("No private key found for: " + alias); } - - /* - * We need to keep reference to this key so it won't get garbage - * collected. If it will then the whole app will crash on Android <= - * 4.2 with "Fatal signal 11 code=1". See - * https://code.google.com/p/android/issues/detail?id=62319 - */ - K9.sClientCertificateReferenceWorkaround.add(key); - return key; } catch (KeyChainException e) { throw new RuntimeException(e); @@ -110,6 +116,15 @@ public class KeyChainKeyManager extends X509ExtendedKeyManager { } } + private synchronized PrivateKey retrieveFirstPrivateKey(String alias) + throws KeyChainException, InterruptedException { + PrivateKey key = KeyChain.getPrivateKey(K9.app, alias); + if (sClientCertificateReferenceWorkaround == null) { + sClientCertificateReferenceWorkaround = key; + } + return key; + } + @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { // not valid for client side