Request with automatic or user selection of appropriate client certificate(请求自动或用户选择适当的客户端证书)
问题描述
我正在开发一个可以连接到不同服务器的混合 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
来配置SSLContext
由 URLConnection
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 连接的代码.它使用默认的 TrustManager
和 HostnameVerifier
.如果服务器使用的是 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!
这篇关于请求自动或用户选择适当的客户端证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:请求自动或用户选择适当的客户端证书
基础教程推荐
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01