请求自动或用户选择适当的客户端证书 [英] Request with automatic or user selection of appropriate client certificate

查看:23
本文介绍了请求自动或用户选择适当的客户端证书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个可以连接到不同服务器的混合 cordova 应用程序.其中一些确实需要客户端证书.

I'm developing an hybrid cordova app which might connect to different servers. Some of them do require a client certificate.

在 Android 手机上安装了相应的根证书 + 客户端证书.

On an Android mobile the corresponding root cert + client certificate is installed.

在 Chrome 浏览器上,我会看到以下对话框,为 Web 连接选择相应的客户端证书.

On Chrome browser I get the following dialog to choose the corresponding client certificate for the Web connection.

使用 cordova 插件 cordova-client-cert-authenticationWebView 中的 Http(s) 请求会弹出相同的对话框.

With the cordova plugin cordova-client-cert-authentication the same dialog pops up for Http(s) requests within the WebView.

我的问题是如何在 原生 Android 平台上实现对 Http(s) 请求的自动证书选择,而无需明确声明相应的客户端证书.或者是否有类似于用户选择证书的功能,例如在 Chrome 上实现的?

My question is how to achieve a automatic certificate selection on Http(s) requests on the native Android platform without explicitly declaring the corresponding client certificate. Or is there something similiar to the user selection of certificate like implemented on Chrome?

这是当前的实现,它会引发握手异常:

This is the current implementation, which throws a handshake exception:

try {
    URL url = new URL( versionUrl );
    HttpsURLConnection urlConnection = ( HttpsURLConnection ) url.openConnection();

    urlConnection.setConnectTimeout( 10000 );

    InputStream in = urlConnection.getInputStream();
}
catch(Exception e)
{
    //javax.net.ssl.SSLHandshakeException: Handshake failed
}

推荐答案

您可以使用之前安装在Android KeyChain(系统密钥库)中的证书扩展X509ExtendedKeyManager来配置SSLContextURLConnection

You can use a certificate previously installed in Android KeyChain (the system key store) extending X509ExtendedKeyManager to configure the SSLContext used by URLConnection

证书由您需要的别名引用.提示用户使用类似于 chrome 的对话框进行选择:

The certificate is referenced by an alias that you need. To prompt user for selection with a dialog similar to chrome use:

KeyChain.choosePrivateKeyAlias(this, this, // Callback
            new String[] {"RSA", "DSA"}, // Any key types.
            null, // Any issuers.
            null, // Any host
            -1, // Any port
            DEFAULT_ALIAS);

这是使用自定义 KeyManager 配置 SSL 连接的代码.它使用默认的 TrustManagerHostnameVerifier.如果服务器使用的是 Android 默认信任库中不存在的自签名证书,则需要配置它们(不建议信任所有证书)

This is the code to configure the SSL connection using a custom KeyManager. It uses the default TrustManager and HostnameVerifier. You will need to configure them if the server is using a self signed certificate not present in Android default truststore (trusting all certificates is not recommended)

//Configure trustManager if needed
TrustManager[] trustManagers = null;

//Configure keyManager to select the private key and the certificate chain from KeyChain
KeyManager keyManager = KeyChainKeyManager.fromAlias(
            context, mClientCertAlias);

//Configure SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[] {keyManager}, trustManagers, null);


//Perform the connection
URL url = new URL( versionUrl );
HttpsURLConnection urlConnection = ( HttpsURLConnection ) url.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
//urlConnection.setHostnameVerifier(hostnameVerifier);  //Configure hostnameVerifier if needed
urlConnection.setConnectTimeout( 10000 );
InputStream in = urlConnection.getInputStream();

您终于有了从 中提取的自定义 X509ExtendedKeyManager 的完整实现在这里这里负责选择客户端证书.我已经提取了所需的代码.

Finally here you have and a full implementation of the custom X509ExtendedKeyManager extracted from here and here that is in charge of selecting the client certificate. I have extracted the required code.

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

        /**
         * Builds an instance of a KeyChainKeyManager using the given certificate alias.
         * If for any reason retrieval of the credentials from the system {@link android.security.KeyChain} fails,
         * a {@code null} value will be returned.
         */
        public static KeyChainKeyManager fromAlias(Context context, String alias)
                throws CertificateException {
            X509Certificate[] certificateChain;
            try {
                certificateChain = KeyChain.getCertificateChain(context, alias);
            } catch (KeyChainException e) {
                throw new CertificateException(e);
            } catch (InterruptedException e) {
                throw new CertificateException(e);
            }

            PrivateKey privateKey;
            try {
                privateKey = KeyChain.getPrivateKey(context, alias);
            } catch (KeyChainException e) {
                throw new CertificateException(e);
            } catch (InterruptedException e) {
                throw new CertificateException(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) {
            return mClientAlias;
        }

        @Override
        public X509Certificate[] getCertificateChain(String alias) {
            return mCertificateChain;
        }

        @Override
        public PrivateKey getPrivateKey(String alias) {
            return mPrivateKey;
        }

         @Override
        public final String chooseServerAlias( String keyType, Principal[] issuers, Socket socket) {
            // not a client SSLSocket callback
            throw new UnsupportedOperationException();
        }

        @Override
        public final String[] getClientAliases(String keyType, Principal[] issuers) {
            // not a client SSLSocket callback
            throw new UnsupportedOperationException();
        }

        @Override
        public final String[] getServerAliases(String keyType, Principal[] issuers) {
            // not a client SSLSocket callback
            throw new UnsupportedOperationException();
        }
    }
}

我没有测试它.报告任何错误!

I did not test it. Report any error!

这篇关于请求自动或用户选择适当的客户端证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆