HttpsUrlConnection不在API 15上使用客户端证书

时间:2016-02-10 10:48:59

标签: android ssl client-certificates httpsurlconnection

我使用客户端证书在我的应用中授权用户。通过KeyChain API获取证书并通过自定义KeyManager将其提供给SSLContext,基于来自标准电子邮件应用程序https://github.com/android/platform_packages_apps_email/blob/master/emailcommon/src/com/android/emailcommon/utility/SSLUtils.java中的SSLUtils的KeyManager实现

URL url = new URL("https://mydomain");
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setSSLSocketFactory(getMySSLSocketFactory());

....

private SSLSocketFactory getMySSLSocketFactory() {
    SSLContext curSSLContext = null;
    String protocol;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        protocol = "TLSv1.2";
    } else {
        protocol = "TLSv1"; //http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
    }
    try {
        curSSLContext = SSLContext.getInstance(protocol);
        curSSLContext.init(getMyKeyManagers(), null, new SecureRandom());
    } catch (Exception e) {...}
    return curSSLContext != null ? curSSLContext.getSocketFactory() : null;
}

private KeyManager[] getMyKeyManagers() {
    managers = new KeyManager[1];
    try {
        managers[0] = KeyChainKeyManager.fromAlias(context, "certAlias");    
    } catch (Exception e) {...}    
    return managers;
}

....

public class KeyChainKeyManager extends StubKeyManager {
    private final String mClientAlias;
    private final X509Certificate[] mCertificateChain;
    private final PrivateKey mPrivateKey;

    public static KeyChainKeyManager fromAlias(Context context, String alias)
            throws CertificateException {
        X509Certificate[] certificateChain;
        try {
            certificateChain = KeyChain.getCertificateChain(context, alias);
        } catch (KeyChainException | InterruptedException e ) {...}
        PrivateKey privateKey;
        try {
            privateKey = KeyChain.getPrivateKey(context, alias);
        } catch (KeyChainException | InterruptedException e) {...}
        if (certificateChain == null || privateKey == null) {
            throw new CertificateException("Can't access certificate from keystore");
        }
        return new KeyChainKeyManager(alias, certificateChain, privateKey);
    }

    private KeyChainKeyManager(String clientAlias, X509Certificate[] certificateChain, PrivateKey privateKey) {
        mClientAlias = clientAlias;
        mCertificateChain = certificateChain;
        mPrivateKey = privateKey;
    }


    @Override
    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
        if (LOG_ENABLED) {
            myLogger.info(...);
        }
        return mClientAlias;
    }

    @Override
    public X509Certificate[] getCertificateChain(String alias) {
        if (LOG_ENABLED) {
            myLogger.info(...);
        }
        return mCertificateChain;
    }

    @Override
    public PrivateKey getPrivateKey(String alias) {
        if (LOG_ENABLED) {
            myLogger.info(...);
        }
        return mPrivateKey;
    }
}

此代码适用于API 16+,但在API 15上,KeyManager的方法从未调用过,服务器也不会收到客户端证书。我在 chooseClientAlias getCertificateChain getPrivateKey 方法中添加了断点,它们在API 16+上触发,而不是在API 15上触发。没有例外或创建KeyManager和SocketFactory时会出错。

设备上的默认浏览器也不能使用客户端证书:它会打开对话框以选择客户端证书并冻结选择。

我在这里找到了可能的原因(帖子112):https://code.google.com/p/android/issues/detail?id=11231#c112但重新启动并没有帮助。

可能与此处描述的问题相同:Client certificate not sent from android to ssl server

这是ICS中的错误还是我做错了什么?

0 个答案:

没有答案