20

我正在调用需要客户端证书身份验证的 Web 服务。

如果我指定一个包含单个证书(服务期望的客户端证书)的 Java 密钥库,那么一切正常。但是,如果我使用包含多个证书的密钥库,那么我似乎无法指定客户端应该获取哪个证书,客户端似乎获取了第一个可用的证书(按字母顺序)。

我尝试了以下属性,但没有预期的结果:

System.setProperty("com.sun.enterprise.security.httpsOutboundKeyAlias", "my-client-certificate alias");

如何指定应使用的客户端证书别名?

4

4 回答 4

16

Jakub在他的回复中提供的链接会引导您找到答案,但我想在这里发布一个更简单的回复,因为我们在这个问题上挣扎了很长一段时间,然后才终于得到了一些有用的东西。

我们有几个证书可供使用的情况,我们需要使用具有特定别名的证书来执行我们的连接。我们通过创建自己的 KeyManager 实现来做到这一点,该实现将其大部分功能传递给默认的 X509KeyManager,但具有在执行连接时准确选择要使用的正确别名的功能。

首先是我们创建的密钥管理器:

public class FilteredKeyManager implements X509KeyManager {

private final X509KeyManager originatingKeyManager;
private final X509Certificate[] x509Certificates;

public FilteredKeyManager(X509KeyManager originatingKeyManager, X509Certificate[] x509Certificates) {
    this.originatingKeyManager = originatingKeyManager;
    this.x509Certificates = x509Certificates;
}

public X509Certificate[] getCertificateChain(String alias) {
    return x509Certificates;
}

public String[] getClientAliases(String keyType, Principal[] issuers) {
    return new String[] {"DesiredClientCertAlias"};
}

实现所需的所有其他方法都是传递到originatingKeyManager.

然后,当我们实际设置上下文时:

SSLContext context = SSLContext.getInstance("TLSv1");
context.init(new KeyManager[] { new FilteredKeyManager((X509KeyManager)originalKeyManagers[0], desiredCertsForConnection) },
    trustManagerFactory.getTrustManagers(), new SecureRandom());

希望这清楚,并适用于其他试图解决这个问题的人。

于 2011-12-29T18:04:30.543 回答
10

简短的回答:它不能用默认的 Java ssl 实现来完成。

长答案:我查看了 SSL 握手是如何在sun.security.ssl.ClientHandshaker. 在它的方法serverHelloDone中被调用X509ExtendedKeyManager.chooseClientAlias。它的实现实际上是以这样的方式完成的,即它们返回第一个别名,其条目与给定的密钥算法和其他一些东西相匹配。无法调整别名选择。

对于那些可以更改代码的人来说,这看起来是一个很有前途的解决方法:http ://www.44342.com/java-f392-t785-p1.htm

于 2011-06-07T15:04:16.073 回答
7

这是一个完整的代码片段,可以工作。

我使用它在 Android 上使用来自智能卡的密钥库创建 SSL 连接,该智能卡包含与标准过滤条件匹配的多个证书。

学分去zarniwoop。

/**
 * filters the SSLCertificate we want to use for SSL
 * <code>
 * KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
 * kmf.init(keyStore, null);
 * String SSLCertificateKeyStoreAlias = keyStore.getCertificateAlias(sslCertificate);
 * KeyManager[] keyManagers = new KeyManager[] { new FilteredKeyManager((X509KeyManager)kmf.getKeyManagers()[0], sslCertificate, SSLCertificateKeyStoreAlias) };
 * </code>
 */
private class FilteredKeyManager implements X509KeyManager {

    private final X509KeyManager originatingKeyManager;
    private final X509Certificate sslCertificate;
    private final String SSLCertificateKeyStoreAlias;

    /**
     * @param originatingKeyManager,       original X509KeyManager
     * @param sslCertificate,              X509Certificate to use
     * @param SSLCertificateKeyStoreAlias, Alias of the certificate in the provided keystore
     */
    public FilteredKeyManager(X509KeyManager originatingKeyManager, X509Certificate sslCertificate, String SSLCertificateKeyStoreAlias) {
        this.originatingKeyManager = originatingKeyManager;
        this.sslCertificate = sslCertificate;
        this.SSLCertificateKeyStoreAlias = SSLCertificateKeyStoreAlias;
    }

    @Override
    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
        return SSLCertificateKeyStoreAlias;
    }

    @Override
    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
        return originatingKeyManager.chooseServerAlias(keyType, issuers, socket);
    }

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

    @Override
    public String[] getClientAliases(String keyType, Principal[] issuers) {
        return originatingKeyManager.getClientAliases(keyType, issuers);
    }

    @Override
    public String[] getServerAliases(String keyType, Principal[] issuers) {
        return originatingKeyManager.getServerAliases(keyType, issuers);
    }

    @Override
    public PrivateKey getPrivateKey(String alias) {
        return originatingKeyManager.getPrivateKey(alias);
    }
}
于 2014-09-19T14:19:18.523 回答
1

我对 KeyManager 的印象是,一旦用 keystore 初始化,它就会使用私钥条目的别名来查找关联的证书和证书链。
否则,我认为它会根据主机识别的密钥类型和证书颁发机构来选择链。
所以在你的情况下,你的描述没有提到密钥库中的私有条目,所以我猜密钥管理器选择了最合适的证书。
我完全不知道您提到的系统属性。
-尝试将密钥库更改为具有私钥和关联的链
-或者(不确定这是否可行)更改要发送到服务器的证书的别名以匹配证书的主题名称

于 2011-03-13T22:04:59.530 回答