Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中选择SSL客户端证书_Java_Web Services_Ssl - Fatal编程技术网

在Java中选择SSL客户端证书

在Java中选择SSL客户端证书,java,web-services,ssl,Java,Web Services,Ssl,我们的系统与多个web服务提供商进行通信。它们都是从单个Java客户端应用程序调用的。到目前为止,所有的web服务都是通过SSL的,但没有一个使用客户端证书。嗯,一个新的合作伙伴正在改变这一点 使应用程序使用证书进行调用很容易;设置javax.net.ssl.keyStore和javax.net.ssl.keystrepassword即可。但是,现在的问题是如何使它只在调用特定web服务时使用证书。我想更一般地说,我们希望能够选择要使用的客户端证书(如果有的话) 一个快速的解决方案是设置系统属性

我们的系统与多个web服务提供商进行通信。它们都是从单个Java客户端应用程序调用的。到目前为止,所有的web服务都是通过SSL的,但没有一个使用客户端证书。嗯,一个新的合作伙伴正在改变这一点

使应用程序使用证书进行调用很容易;设置
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/SSL
CertificateRequest
消息,其中包含它愿意接受的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));