java 如何为 HTTPS 调用指定出站证书别名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5292074/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to specify outbound certificate alias for HTTPS-calls?
提问by aksamit
I am calling a webservice that requires client certificate authentication.
我正在调用需要客户端证书身份验证的 Web 服务。
If I specify a Java keystore containing a single certificate (the client certificate the service expects) then everything works fine. However if I use a keystore that contains multiple certificates then I can't seem to be able to specify which certificate should be picked up by the client, the client seems to pick up the first available certificate (in alphabetical order).
如果我指定一个包含单个证书(服务期望的客户端证书)的 Java 密钥库,那么一切正常。但是,如果我使用包含多个证书的密钥库,那么我似乎无法指定客户端应该选择哪个证书,客户端似乎选择第一个可用证书(按字母顺序)。
I have tried the following property but without expected result:
我尝试了以下属性但没有预期的结果:
System.setProperty("com.sun.enterprise.security.httpsOutboundKeyAlias", "my-client-certificate alias");
How can I specify which client certificate alias that should be used?
如何指定应该使用哪个客户端证书别名?
采纳答案by Jakub
Short answer: it cannot be done with default Java ssl implementation.
简短的回答:它不能用默认的 Java ssl 实现来完成。
Long answer: I looked on how the SSL handshake is implemented in the sun.security.ssl.ClientHandshaker
. In its method serverHelloDone
is called X509ExtendedKeyManager.chooseClientAlias
. Its implementations are really done in such a way that they return first alias, whose entry matches the given key algorithm and few other things. No way how to tweak the alias selection.
长答案:我查看了 SSL 握手是如何在sun.security.ssl.ClientHandshaker
. 在其方法serverHelloDone
中称为X509ExtendedKeyManager.chooseClientAlias
. 它的实现实际上是以这样一种方式完成的,即它们返回第一个别名,其条目与给定的密钥算法和其他一些东西匹配。没有办法如何调整别名选择。
For those who can change the code, this looks like a promising workaround: http://www.44342.com/java-f392-t785-p1.htm
对于那些可以更改代码的人来说,这看起来是一个很有前途的解决方法:http: //www.44342.com/java-f392-t785-p1.htm
回答by zarniwoop
The links that Jakubprovides in his responselead you to the answer, but I wanted to post a simpler response here, since we struggled with this problem for quite a while before finally getting something that worked.
Jakub在他的回复中提供的链接会引导您找到答案,但我想在这里发布一个更简单的回复,因为我们在这个问题上挣扎了很长时间,最后才得到了一些有用的东西。
We have the case where there are several certificates available to use, and we need to use the one that has a specific alias to perform our connection. We did this by creating our own KeyManager implementation which passes through most of its functionality to the default X509KeyManager but has functionality to select exactly the correct alias to use when the connection is performed.
我们遇到过有多个证书可供使用的情况,我们需要使用具有特定别名的证书来执行我们的连接。我们通过创建我们自己的 KeyManager 实现来做到这一点,该实现将其大部分功能传递给默认的 X509KeyManager,但具有在执行连接时准确选择要使用的正确别名的功能。
First the key manager we created:
首先是我们创建的密钥管理器:
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"};
}
All other methods required for implementation are passthroughs to originatingKeyManager
.
实现所需的所有其他方法都传递到originatingKeyManager
.
Then, when we actually set up the context:
然后,当我们实际设置上下文时:
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(new KeyManager[] { new FilteredKeyManager((X509KeyManager)originalKeyManagers[0], desiredCertsForConnection) },
trustManagerFactory.getTrustManagers(), new SecureRandom());
Hope that makes it clear, and works for anyone else trying to solve this problem.
希望这能说明一切,并适用于试图解决此问题的任何其他人。
回答by Frankie
Heres a complete code snipped that works.
这是一个完整的代码片段,可以工作。
I use it to create an SSL connection on Android with a keystore from a smartcard that contains multiple certificates that match the standard filtering criteria.
我使用它在 Android 上创建 SSL 连接,其中包含来自智能卡的密钥库,该智能卡包含多个符合标准过滤条件的证书。
Credits go to zarniwoop.
学分转到 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);
}
}
回答by Cratylus
My impression of the KeyManager is that once it is initialized with the keystore, it uses the alias of the private key entry to find the associated certificate and certificate chain.
Otherwise,I think it chooses a chain based on the key types and certificate authorities recognized by the host.
So in your case, your description does not mention a private entry in the keystore, so I will guess that the keymanager chooses the most suitable certificate.
I am not aware at all of the system property you mention.
-Try to change the keystore to have a private key and the associated chain
-Or (not sure if this will work) change the alias of the certificate you want to send to the server to match the subject name of the certificate
我对 KeyManager 的印象是,一旦用密钥库初始化它,它就会使用私钥条目的别名来查找关联的证书和证书链。
否则,我认为它会根据主机认可的密钥类型和证书颁发机构来选择链。
所以在你的情况下,你的描述没有提到密钥库中的私有条目,所以我猜密钥管理器会选择最合适的证书。
我完全不知道您提到的系统属性。
- 尝试更改密钥库以拥有私钥和关联的链 - 或者
(不确定这是否可行)更改要发送到服务器的证书的别名以匹配证书的主题名称