[英]Choosing SSL client certificate in Java
我们的系统与多个网络服务提供商进行通信。 它们都是从单个 Java 客户端应用程序调用的。 到目前为止,所有 Web 服务都通过 SSL,但没有一个使用客户端证书。 好吧,一个新的合作伙伴正在改变这一点。
让应用程序使用证书进行调用很容易; 设置javax.net.ssl.keyStore
和javax.net.ssl.keyStorePassword
就可以了。 然而,现在的问题是如何使它只在调用该特定 Web 服务时使用证书。 我想更一般地说,我们希望能够选择要使用的客户端证书(如果有的话)。
一种快速的解决方案是设置系统属性,调用方法,然后取消设置。 唯一的问题是我们正在处理一个多线程应用程序,所以现在我们需要处理同步或锁或你有什么。
每个服务客户端都应该彼此完全独立,并且它们被单独打包在单独的 JAR 中。 因此,我想到的一个选择(尽管我们还没有正确分析它)是以某种方式隔离每个 JAR,也许将每个 JAR 加载到具有不同参数的不同 VM 下。 这只是一个我不知道如何实现的想法(或者如果它甚至可能,就此而言。)
这篇文章表明可以从密钥库中选择一个单独的证书,但是如何将其附加到请求中似乎完全是一个不同的问题。
我们正在使用 Java 1.5、Axis2 和使用wsimport
或wsdl2java
生成的客户端类。
配置是通过SSLContext
完成的,它实际上是SSLSocketFactory
(或SSLEngine
)的工厂。 默认情况下,这将通过javax.net.ssl.*
属性进行配置。 此外,当服务器请求证书时,它会发送一条 TLS/SSL CertificateRequest
消息,其中包含它愿意接受的 CA 可分辨名称列表。 虽然这个列表严格来说只是指示性的(即服务器可以接受来自不在列表中的颁发者的证书,或者可以拒绝来自列表中的 CA 的有效证书),但它通常以这种方式工作。
默认情况下,在SSLContext
中配置的X509KeyManager
中的证书选择器(同样您通常不必担心),将选择列表中的一个已颁发的证书之一(或者可以链接到颁发者那里)。 该列表是X509KeyManager.chooseClientAlias
issuers
( alias
是您要选择的证书的别名,如密钥库中所指)。 如果您有多个候选人,您还可以使用socket
参数,如果这有助于您做出选择,它将获得对等方的 IP 地址。
如果这有帮助,您可能会发现使用jSSLutils(及其包装器)来配置SSLContext
(这些主要是构建SSLContext
的辅助类)。 (注意这个例子是选择服务器端的别名,但可以修改, 源代码可用。)
完成此操作后,您应该在 Axis(和
SecureSocketFactory
)中查找有关
axis.socketSecureFactory
系统属性的文档。
如果您查看 Axis 源代码,构建一个从您选择的
SSLContext
初始化的
org.apache.axis.components.net.SunJSSESocketFactory
应该不会太难(参见
这个问题)。
刚刚意识到您在谈论 Axis2, SecureSocketFactory
似乎已经消失了。 您也许可以使用默认的SSLContext
找到解决方法,但这会影响您的整个应用程序(这不是很好)。 如果您使用jSSLutils的 X509KeyManagerWrapper,您可能能够使用默认的X509KeyManager
并且仅将某些主机视为例外。 (这不是一个理想的情况,我不确定如何在 Axis 2 中使用自定义SSLContext
/ SSLSocketFactory
。)
或者,根据 此 Axis 2 文档,看起来 Axis 2 使用 Apache HTTP Client 3.x:
如果你想进行 SSL 客户端认证(2-way SSL),你可以使用 HttpClient 的 Protocol.registerProtocol 特性。 如果您不想与常规 https 混淆,您可以覆盖“https”协议,或者为您的 SSL 客户端身份验证通信使用不同的协议。 在http://jakarta.apache.org/commons/httpclient/sslguide.html找到更多信息
在这种情况下, SslContextedSecureProtocolSocketFactory
应该帮助您配置SSLContext
。
Java SSL 客户端仅在服务器请求时才会发送证书。 服务器可以发送关于它将接受哪些证书的可选提示; 如果有多个证书,这将帮助客户选择一个证书。
通常,使用特定客户端证书创建新的SSLContext
,并从从该上下文获取的工厂创建Socket
实例。 不幸的是,Axis2 似乎不支持使用SSLContext
或自定义SocketFactory
。 它的客户端证书设置是全局的。
我为不同的端点初始化了 EasySSLProtocolSocketFactory和 Protocol 实例,并使用这样的唯一键注册协议:
/**
* This method does the following:
* 1. Creates a new and unique protocol for each SSL URL that is secured by client certificate
* 2. Bind keyStore related information to this protocol
* 3. Registers it with HTTP Protocol object
* 4. Stores the local reference for this custom protocol for use during furture collect calls
*
* @throws Exception
*/
public void registerProtocolCertificate() throws Exception {
EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory();
easySSLPSFactory.setKeyMaterial(createKeyMaterial());
myProtocolPrefix = (HTTPS_PROTOCOL + uniqueCounter.incrementAndGet());
Protocol httpsProtocol = new Protocol(myProtocolPrefix,(ProtocolSocketFactory) easySSLPSFactory, port);
Protocol.registerProtocol(myProtocolPrefix, httpsProtocol);
log.trace("Protocol [ "+myProtocolPrefix+" ] registered for the first time");
}
/**
* Load keystore for CLIENT-CERT protected endpoints
*/
private KeyMaterial createKeyMaterial() throws GeneralSecurityException, Exception {
KeyMaterial km = null;
char[] password = keyStorePassphrase.toCharArray();
File f = new File(keyStoreLocation);
if (f.exists()) {
try {
km = new KeyMaterial(keyStoreLocation, password);
log.trace("Keystore location is: " + keyStoreLocation + "");
} catch (GeneralSecurityException gse) {
if (logErrors){
log.error("Exception occured while loading keystore from the following location: "+keyStoreLocation, gse);
throw gse;
}
}
} else {
log.error("Unable to load Keystore from the following location: " + keyStoreLocation );
throw new CollectorInitException("Unable to load Keystore from the following location: " + keyStoreLocation);
}
return km;
}
当我必须调用 Web 服务时,我会这样做(基本上将 URL 中的“https”替换为 https1、https2 或其他内容,具体取决于您为该特定端点初始化的协议):
httpClient.getHostConfiguration().setHost(host, port,Protocol.getProtocol(myProtocolPrefix));
initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix));
它就像一个魅力!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.