问题描述
我以前曾问过这个问题,但没有得到任何帮助。 我已经取得了一些进步,但是又被一个新的错误卡住了。
我必须使用保存密钥对和CA证书保存的安全设备。 现在,当应用程序与需要相互身份验证的服务器建立TLS连接时,我必须使用此设备。 我有一个“ KeyPair”类,可以与安全设备通信。
我的问题是,建立ssl连接后,我在客户端收到错误“ tlsv1警报未知ca”,在服务器上收到“ SSL3_GET_CLIENT_CERTIFICATE:未返回证书”。 我的调试日志显示SSL上下文确实以信任管理器的身份查询我的对象。 首先,我的checkServerTrusted()函数被调用通过。 接下来,将调用chooseClientAlias()函数,然后调用带有客户机别名的getPrivateKey()函数。 此时,我从设备中提取私钥并返回它。 接下来,调用getCertificateChain()函数,然后获取并返回数组中的匹配证书及其签名者CA。 我的调试输出显示证书返回为:
Subject DN: CN=be2576a357228b303189ab62bd2497807d2276493ddfc6fd037a0c8fc6e9ac9a,C=YY,E=a@b.com
Issuer DN: O=myCompany,E=email@mydomain.net,L=myCity,ST=myState,C=XX,CN=LJB
Serial Num = 3877882041
Subject DN = O=myCompany,E=email@mydomain.net,L=myCity,ST=myState,C=XX,CN=LJB
Issuer DN = O=myCompany,E=email@mydomain.net,L=myCity,ST=myState,C=XX,CN=LJB
Serial Num = 10069430368951295741
然后,SSL连接会暂停,然后失败,并出现上述错误。
我扩展了“ KeyPair”类,使其像这样的密钥和信任管理器:
public class Keypair implements X509KeyManager, X509TrustManager
在此类中,我重写了这些管理器的所有功能,以查看何时调用什么。 我的应用程序将永远是在服务器上工作的客户端。
因此,在创建此类对象期间以及在设备上对密钥对进行所有测试之后,我将创建一个使用特定密钥对的SSL上下文。 目前,设备上只有一对密钥。 我用,...
sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(new KeyManager[] {this}, new TrustManager[] {this}, new SecureRandom());
稍后,任务将使用该对象创建与服务器的连接
protected boolean Connect(String host, int port) {
SSLSocketFactory factory = getKeypair().getSecureSocketFactory();
socket = (SSLSocket) factory.createSocket(host, port);
:
}
其中的getSecureSocketFactory()函数只是从密钥对类对象返回sslCtx.getSocketFactory()。
我已经搜索了很多,并看到了创建工厂等的代码,但是没有一个能够帮助我。 我不想创建任何密钥库-这是设备的目的。
那么错误指的是哪个“ CA”? 服务器证书也由上述CA直接签名。 如果我不需要客户端进行身份验证,则连接有效。 我正在使用Android 4.1.2的手机进行测试。
后续:我确定我的问题出在我的getCertificateChain()函数上。 我只包括从安全设备中检索到的公共证书和CA证书。 它永远不要使用Android随附的任何证书。 因此,有人能给我一些有关归还链条的规则的指示吗? 我是否正确理解“链”:这必须是与私钥匹配的证书,以及直到主CA(自签名)之前的所有证书?”
1楼
为了设置您的TrustManager,我使用了一些在某处找到的实用程序功能:
void trustCertificate(X509Certificate cert) {
if (cert!=null) {
try {
KeyStore.TrustedCertificateEntry x = new KeyStore.TrustedCertificateEntry(cert);
sslKeystore.setEntry(cert.getSubjectDN().getName(), x, null);
} catch (KeyStoreException e) {
e.printStackTrace();
}
saveKeystore();
}
}
private void createKeystore() {
try {
sslKeystore.load(null,null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null);
// Copy current certs into our keystore so we can use it...
// TODO: don't actually do this...
X509TrustManager xtm = (X509TrustManager) tmf.getTrustManagers()[0];
for (X509Certificate cert : xtm.getAcceptedIssuers()) {
sslKeystore.setCertificateEntry(cert.getSubjectDN().getName(), cert);
}
} catch (Exception e) {
e.printStackTrace();
}
saveKeystore();
}
private void saveKeystore() {
try {
sslKeystore.store(new FileOutputStream(keystoreFile), KEYSTORE_PASSWORD.toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
}
并来自调用类:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(cafile);
Certificate ca = null;
try {
ca = cf.generateCertificate(caInput);
} catch(Exception e) {
}
finally {
caInput.close();
}
certManagerCA.trustCertificate((X509Certificate) ca);
KeyStore keyStoreCA = certManagerCA.sslKeystore;
tmf = TrustManagerFactory.getInstance("X509");
tmf.init(keyStoreCA);