我使用客户端证书在我的应用中授权用户。通过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中的错误还是我做错了什么?