60

我正在一个分布式应用程序中的服务器上工作,该应用程序具有浏览器客户端,并且还参与与第 3 方的服务器到服务器通信。我的服务器有一个 CA 签名证书,让我的客户端使用 HTTP/S 和 XMPP(安全)使用 TLS (SSL) 通信进行连接。这一切都很好。

现在我需要通过 HTTPS/SSL 使用 JAX-WS 安全地连接到第 3 方服务器。在此通信中,我的服务器在 JAX-WS 交互中充当客户端,并且我有一个由第 3 方签署的客户端证书。

我尝试通过标准系统配置 ( -Djavax.net.ssl.keyStore=xyz) 添加新的密钥库,但我的其他组件显然受此影响。尽管我的其他组件对其 SSL 配置 ( my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...) 使用专用参数,但似乎它们最终使用全局SSLContext. (配置命名空间my.xmpp.似乎表明了分离,但事实并非如此)

我还尝试将我的客户端证书添加到我的原始密钥库中,但是 - 再次 - 我的其他组件似乎也不喜欢它。

我认为我剩下的唯一选择是以编程方式连接到 JAX-WS HTTPS 配置,以设置用于客户端 JAX-WS 交互的密钥库和信任库。

关于如何做到这一点的任何想法/指示?我找到的所有信息要么使用该javax.net.ssl.keyStore方法,要么设置全局SSLContext,我猜这将在同一个confilc中结束。我最接近有用的是这个旧的错误报告,它请求我需要的功能:添加对将 SSLContext 传递给 JAX-WS 客户端运行时的支持

有什么需要吗?

4

10 回答 10

55

这是一个难以破解的难题,因此记录在案:

为了解决这个问题,它需要一个 customKeyManager和一个SSLSocketFactory使用这个 customKeyManager来访问分隔的KeyStore. KeyStore我在这个优秀的SSLFactory博客条目 中找到了基本代码: how-to-dynamically-select-a-certificate-alias-when-invoking-web-services

然后,需要将专门SSLSocketFactory的内容插入到 WebService 上下文中:

service = getWebServicePort(getWSDLLocation());
BindingProvider bindingProvider = (BindingProvider) service; 
bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory()); 

其中getCustomSocketFactory()返回SSLSocketFactory使用上述方法创建的。这仅适用于 JDK 中内置的 Sun-Oracle impl 中的 JAX-WS RI,因为指示SSLSocketFactory属性的字符串是此实现专有的。

在这个阶段,JAX-WS 服务通信是通过 SSL 保护的,但是如果您从同一个安全服务器 () 加载 WSDL,那么您将遇到引导问题,因为收集 WSDL 的 HTTPS 请求将不会使用与 Web 服务相同的凭据。我通过使 WSDL 在本地可用(file:///...)并动态更改 Web 服务端点来解决这个问题:(关于为什么需要这样做的很好的讨论可以在这个论坛中找到)

bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, webServiceLocation); 

现在,WebService 被引导并能够使用命名(别名)客户端证书和相互身份验证通过 SSL 与服务器对应方进行通信。∎</p>

于 2012-06-14T11:50:57.807 回答
22

这就是我根据这篇文章通过一些小的调整来解决它的方法。此解决方案不需要创建任何其他类。

SSLContext sc = SSLContext.getInstance("SSLv3");

KeyManagerFactory kmf =
    KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );

KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
ks.load(new FileInputStream( certPath ), certPasswd.toCharArray() );

kmf.init( ks, certPasswd.toCharArray() );

sc.init( kmf.getKeyManagers(), null, null );

((BindingProvider) webservicePort).getRequestContext()
    .put(
        "com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory",
        sc.getSocketFactory() );
于 2012-09-17T17:32:35.300 回答
21

我尝试了以下方法,但在我的环境中不起作用:

bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", getCustomSocketFactory());

但是不同的属性就像一个魅力:

bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, getCustomSocketFactory());

其余代码取自第一个回复。

于 2012-10-30T13:30:04.830 回答
5

通过结合 Radek 和 l0co 的答案,您可以访问 https 后面的 WSDL:

SSLContext sc = SSLContext.getInstance("TLS");

KeyManagerFactory kmf = KeyManagerFactory
        .getInstance(KeyManagerFactory.getDefaultAlgorithm());

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(getClass().getResourceAsStream(keystore),
        password.toCharArray());

kmf.init(ks, password.toCharArray());

sc.init(kmf.getKeyManagers(), null, null);

HttpsURLConnection
        .setDefaultSSLSocketFactory(sc.getSocketFactory());

yourService = new YourService(url); //Handshake should succeed
于 2015-07-23T21:01:10.937 回答
3

您可以将您的代理身份验证和 ssl 人员移动到肥皂处理程序

  port = new SomeService().getServicePort();
  Binding binding = ((BindingProvider) port).getBinding();
  binding.setHandlerChain(Collections.<Handler>singletonList(new ProxyHandler()));

这是我的例子,做所有的网络操作

  class ProxyHandler implements SOAPHandler<SOAPMessageContext> {
    static class TrustAllHost implements HostnameVerifier {
      public boolean verify(String urlHostName, SSLSession session) {
        return true;
      }
    }

    static class TrustAllCert implements X509TrustManager {
      public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
      }

      public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
      }

      public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
      }
    }

    private SSLSocketFactory socketFactory;

    public SSLSocketFactory getSocketFactory() throws Exception {
      // just an example
      if (socketFactory == null) {
        SSLContext sc = SSLContext.getInstance("SSL");
        TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllCert() };
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        socketFactory = sc.getSocketFactory();
      }

      return socketFactory;
    }

    @Override public boolean handleMessage(SOAPMessageContext msgCtx) {
      if (!Boolean.TRUE.equals(msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)))
        return true;

      HttpURLConnection http = null;

      try {
        SOAPMessage outMessage = msgCtx.getMessage();
        outMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
        // outMessage.setProperty(SOAPMessage.WRITE_XML_DECLARATION, true); // Not working. WTF?

        ByteArrayOutputStream message = new ByteArrayOutputStream(2048);
        message.write("<?xml version='1.0' encoding='UTF-8'?>".getBytes("UTF-8"));
        outMessage.writeTo(message);

        String endpoint = (String) msgCtx.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
        URL service = new URL(endpoint);

        Proxy proxy = Proxy.NO_PROXY;
        //Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("{proxy.url}", {proxy.port}));

        http = (HttpURLConnection) service.openConnection(proxy);
        http.setReadTimeout(60000); // set your timeout
        http.setConnectTimeout(5000);
        http.setUseCaches(false);
        http.setDoInput(true);
        http.setDoOutput(true);
        http.setRequestMethod("POST");
        http.setInstanceFollowRedirects(false);

        if (http instanceof HttpsURLConnection) {
          HttpsURLConnection https = (HttpsURLConnection) http;
          https.setHostnameVerifier(new TrustAllHost());
          https.setSSLSocketFactory(getSocketFactory());
        }

        http.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
        http.setRequestProperty("Content-Length", Integer.toString(message.size()));
        http.setRequestProperty("SOAPAction", "");
        http.setRequestProperty("Host", service.getHost());
        //http.setRequestProperty("Proxy-Authorization", "Basic {proxy_auth}");

        InputStream in = null;
        OutputStream out = null;

        try {
          out = http.getOutputStream();
          message.writeTo(out);
        } finally {
          if (out != null) {
            out.flush();
            out.close();
          }
        }

        int responseCode = http.getResponseCode();
        MimeHeaders responseHeaders = new MimeHeaders();
        message.reset();

        try {
          in = http.getInputStream();
          IOUtils.copy(in, message);
        } catch (final IOException e) {
          try {
            in = http.getErrorStream();
            IOUtils.copy(in, message);
          } catch (IOException e1) {
            throw new RuntimeException("Unable to read error body", e);
          }
        } finally {
          if (in != null)
            in.close();
        }

        for (Map.Entry<String, List<String>> header : http.getHeaderFields().entrySet()) {
          String name = header.getKey();

          if (name != null)
            for (String value : header.getValue())
              responseHeaders.addHeader(name, value);
        }

        SOAPMessage inMessage = MessageFactory.newInstance()
          .createMessage(responseHeaders, new ByteArrayInputStream(message.toByteArray()));

        if (inMessage == null)
          throw new RuntimeException("Unable to read server response code " + responseCode);

        msgCtx.setMessage(inMessage);
        return false;
      } catch (Exception e) {
        throw new RuntimeException("Proxy error", e);
      } finally {
        if (http != null)
          http.disconnect();
      }
    }

    @Override public boolean handleFault(SOAPMessageContext context) {
      return false;
    }

    @Override public void close(MessageContext context) {
    }

    @Override public Set<QName> getHeaders() {
      return Collections.emptySet();
    }
  }

它使用 UrlConnection,您可以在处理程序中使用您想要的任何库。玩得开心!

于 2018-11-20T21:20:36.393 回答
2

上述内容很好(正如我在评论中所说),除非您的 WSDL 也可以通过 https:// 访问。

这是我的解决方法:

将 SSLSocketFactory 设置为默认值:

HttpsURLConnection.setDefaultSSLSocketFactory(...);

对于我使用的 Apache CXF,您还需要将这些行添加到您的配置中:

<http-conf:conduit name="*.http-conduit">
  <http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" />
<http-conf:conduit>
于 2013-10-25T10:40:11.390 回答
1

对于那些尝试但仍未使其正常工作的人,这使用 Wildfly 8 为我做到了,使用动态Dispatcher

bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", yourSslSocketFactory);

请注意,internalProperty 键中的部分已在此处消失。

于 2014-04-14T06:58:32.123 回答
0

在设置信任管理器时,我在信任自签名证书时遇到了问题。我使用了 apache httpclient 的 SSLContexts 构建器来创建一个自定义SSLSocketFactory

SSLContext sslcontext = SSLContexts.custom()
        .loadKeyMaterial(keyStoreFile, "keystorePassword.toCharArray(), keyPassword.toCharArray())
        .loadTrustMaterial(trustStoreFile, "password".toCharArray(), new TrustSelfSignedStrategy())
        .build();
SSLSocketFactory customSslFactory = sslcontext.getSocketFactory()
bindingProvider.getRequestContext().put(JAXWSProperties.SSL_SOCKET_FACTORY, customSslFactory);

new TrustSelfSignedStrategy()并在方法中作为参数传入loadTrustMaterial

于 2017-06-12T21:00:35.743 回答
0

我在这里尝试了这些步骤:

http://jyotirbhandari.blogspot.com/2011/09/java-error-invalidalgorithmparameterexc.html

而且,这解决了这个问题。我做了一些小的调整 - 我使用 System.getProperty 设置了两个参数...

于 2017-06-23T17:17:26.000 回答
0

由于系统集成之间的密钥库冲突,我们遇到了这个问题,因此我们使用了以下代码。

private PerSecurityWS prepareConnectionPort()  {
      final String HOST_BUNDLE_SYMBOLIC_NAME = "wpp.ibm.dailyexchangerates";
      final String PATH_TO_SLL = "ssl/<your p.12 certificate>";
      final File ksFile = getFile(HOST_BUNDLE_SYMBOLIC_NAME, PATH_TO_SLL);
      final String serverURI = "you url";


      final KeyStore keyStore = KeyStore.getInstance("pkcs12");
      keyStore.load(new FileInputStream(ksFile.getAbsolutePath()), keyStorePassword.toCharArray());
      final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      kmf.init(keyStore, keyStorePassword.toCharArray());

      final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
        @Override
        public boolean verify(final String hostname, final SSLSession session) {
          return false;
        }
      };

      final SSLContext ctx = SSLContext.getInstance("TLS");
      ctx.init(kmf.getKeyManagers(), null, null);
      final SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();

      final PerSecurityWS port = new PerSecurityWS_Service().getPerSecurityWSPort();

      final BindingProvider bindingProvider = (BindingProvider) port;
      bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory",sslSocketFactory);
      bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serverURI);
      bindingProvider.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.hostname.verifier",DO_NOT_VERIFY);
      return port;
    }
于 2021-10-30T11:44:42.157 回答