在Java中选择SSL客户端证书
我们的系统与多个web服务提供商进行通信。它们都是从单个Java客户端应用程序调用的。到目前为止,所有的web服务都是通过SSL的,但没有一个使用客户端证书。嗯,一个新的合作伙伴正在改变这一点 使应用程序使用证书进行调用很容易;设置在Java中选择SSL客户端证书,java,web-services,ssl,Java,Web Services,Ssl,我们的系统与多个web服务提供商进行通信。它们都是从单个Java客户端应用程序调用的。到目前为止,所有的web服务都是通过SSL的,但没有一个使用客户端证书。嗯,一个新的合作伙伴正在改变这一点 使应用程序使用证书进行调用很容易;设置javax.net.ssl.keyStore和javax.net.ssl.keystrepassword即可。但是,现在的问题是如何使它只在调用特定web服务时使用证书。我想更一般地说,我们希望能够选择要使用的客户端证书(如果有的话) 一个快速的解决方案是设置系统属性
javax.net.ssl.keyStore
和javax.net.ssl.keystrepassword
即可。但是,现在的问题是如何使它只在调用特定web服务时使用证书。我想更一般地说,我们希望能够选择要使用的客户端证书(如果有的话)
一个快速的解决方案是设置系统属性,调用方法,然后取消设置它们。唯一的问题是,我们正在处理一个多线程应用程序,所以现在我们需要处理同步、锁或其他问题
每个服务客户机应该彼此完全独立,并且它们分别打包在单独的jar中。因此,我想到的一个选择(尽管我们没有正确地分析它)是以某种方式隔离每个JAR,也许用不同的参数将每个JAR加载到不同的VM下。这只是一个我不知道如何实现的想法(或者说,如果可能的话)
建议可以从密钥存储中选择单个证书,但如何将其附加到请求似乎是完全不同的问题
我们正在使用Java 1.5、Axis2和通过
wsimport
或wsdl2java
生成的客户端类,Java SSL客户端仅在服务器请求时发送证书。服务器可以发送关于它将接受什么证书的可选提示;如果客户端有多个证书,这将帮助客户端选择单个证书
通常,使用特定的客户端证书创建新的
SSLContext
,并从从该上下文获得的工厂中创建Socket
实例。不幸的是,Axis2似乎不支持使用SSLContext
或自定义SocketFactory
。它的客户端证书设置是全局的。配置是通过SSLContext
完成的,它实际上是SSLSocketFactory
(或SSLEngine
)的工厂。默认情况下,这将通过javax.net.ssl.*
属性进行配置。此外,当服务器请求证书时,它会发送一条TLS/SSLCertificateRequest
消息,其中包含它愿意接受的CA可分辨名称的列表。虽然严格来说,此列表只是指示性的(即服务器可以接受列表中未列出的颁发者的证书,或者可以拒绝列表中CA的有效证书),但它通常是这样工作的
默认情况下,在SSLContext
中配置的X509KeyManager
中的证书选择器(您通常也不必担心它)将选择列表中某个颁发者颁发的证书之一(或者可以链接到那里的颁发者)。
该列表是中的issuers
参数(alias
是您要拾取的证书的别名,如密钥库中所述)。如果您有多个候选者,您还可以使用socket
参数,如果这有助于做出选择,它将获取对等方的IP地址
如果这有帮助,您可以找到用于配置SSLContext
(这些主要是用于构建SSLContext
s的帮助器类)。(请注意,此示例用于选择服务器端别名,但也可以对其进行调整。)
完成此操作后,应在axis(和)中查找有关axis.socketSecureFactory
系统属性的文档。如果您查看Axis源代码,构建从您选择的SSLContext
初始化的org.apache.Axis.components.net.SunJSSESocketFactory
应该不会太困难(请参阅)
刚刚意识到您在谈论Axis2,SecureSocketFactory
似乎已经消失了。您可能可以使用默认的SSLContext
找到解决方法,但这会影响整个应用程序(这不是很好)。如果您使用一组jSSLutils,您可能可以使用默认的X509KeyManager
,并且只将某些主机视为例外。(这不是一个理想的情况,我不知道如何在轴2中使用自定义的SSLContext
/SSLSocketFactory
)
或者,根据,Axis2似乎使用ApacheHTTP客户端3.x:
如果要执行SSL客户端
身份验证(双向SSL),您可以
使用Protocol.registerProtocol
HttpClient的特性。你可以
覆盖“https”协议,或使用
SSL的不同协议
客户端身份验证通信
如果你不想打乱常规的
https。有关更多信息,请访问
在这种情况下,应帮助您为不同端点配置SSLContext
I初始化和协议实例,并使用唯一密钥注册协议,如下所示:
/**
* 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服务时,我会这样做(这基本上是用https1或https2或其他内容替换URL中的“https”,具体取决于您为该特定端点初始化的协议):
它就像一个符咒 不幸的是,我们无法控制服务器。我还希望这些提示可能有用,但测试表明,至少在一台服务器上没有。WRT Axis2,这也是我的经验。如果您碰巧知道另一个Java工具允许这种行为,我将非常感谢您的分享;我们很乐意去探索。@Carlos-也许我不清楚。您不需要对服务器进行任何控制;如果你的搭档
httpClient.getHostConfiguration().setHost(host, port,Protocol.getProtocol(myProtocolPrefix));
initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix));