Вопрос по java, jax-ws, certificate, ssl – Как программно установить SSLContext клиента JAX-WS?

52

Я работаю на сервере в распределенном приложении, которое имеет браузерные клиенты, а также участвует в межсерверной связи с третьей стороной. У моего сервера есть сертификат, подписанный ЦС, который позволяет моим клиентам подключаться по протоколу TLS (SSL) с использованием HTTP / S и XMPP (безопасный). Это все работает нормально.

Теперь мне нужно безопасно подключиться к стороннему серверу, используя JAX-WS через HTTPS / SSL. В этом сообщении мой сервер действует как клиент в взаимодействии JAX-WS, а я - клиентский сертификат, подписанный третьей стороной.

Я попытался добавить новое хранилище ключей через стандартную конфигурацию системы (-Djavax.net.ssl.keyStore=xyz) но другие мои компоненты явно затронуты этим. Хотя мои другие компоненты используют выделенные параметры для их конфигурации SSL (my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...), кажется, что они в конечном итоге используют глобальныйSSLContext, (Пространство имен конфигурацииmy.xmpp. казалось, указывает на разделение, но это не так)

Я также попытался добавить свой клиентский сертификат в исходное хранилище ключей, но, возможно, другим моим компонентам он тоже не понравился.

Я думаю, что мой единственный оставшийся вариант - программно подключиться к конфигурации HTTPS JAX-WS для настройки хранилища ключей и доверенных сертификатов для взаимодействия клиента JAX-WS.

Любые идеи / указатели о том, как это сделать? Вся информация, которую я нахожу, либо используетjavax.net.ssl.keyStore метод или установка глобальногоSSLContext что, я думаю, все закончится тем же конфликтом. Самым близким, что я получил к чему-то полезному, был этот старый отчет об ошибках, который запрашивает нужную мне функцию:Добавить поддержку для передачи SSLContext в среду выполнения клиента JAX-WS

Любой берет?

В отчете об ошибке говорится, что исправление будет доступно в 2.1.1. user207421
@EJP Этот отчет об ошибке - скорее запрос функции, в котором не указано, как это должно быть сделано. maasg

Ваш Ответ

9   ответов
19

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

Но другая собственность работала как шарм:

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

Остальная часть кода была взята из первого ответа.

Когда вы явно добавляете JAR-файлы jaxws-rt в свое приложение, вам нужно использовать имена свойств, которые содержатся в DON'T.internal., Если вы используете JAXWS-RT, включенный в JDK, вам нужно использовать те, которые содержат & quot; .internal. & Quot ;.JAXWSProperties.SSL_SOCKET_FACTORY решено"com.sun.xml.ws.transport.https.client.SSLSocketFactory"
Какая версия всего? Похоже, разные версииwsimport генерировать разные виды кода ... возможно, что им также требуются разные имена свойств.
2

если ваш WSDL не доступен и с https: //.

Вот мой обходной путь для этого:

Установите SSLSocketFactory по умолчанию:

HttpsURLConnection.setDefaultSSLSocketFactory(...);

Для Apache CXF, который я использую, вам также необходимо добавить эти строки в ваш конфиг:

<http-conf:conduit name="*.http-conduit">
  <http-conf:tlsClientParameters useHttpsURLConnectionDefaultSslSocketFactory="true" />
<http-conf:conduit>
0

ботчик мыла

  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, вы можете использовать любую библиотеку, которую вы хотите в обработчике. Повеселись!

0

кто пытается и до сих пор не работает, это было сделано для меня с Wildfly 8, используя динамическийDispatcher:

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

Обратите внимание, чтоinternal часть от Ключа собственности пропала здесь.

Я использую Wildfly 8, и у меня это тоже не работает. (Что вы имеете в виду сdynamic Dispatcher? - Пожалуйста, проверьте мои сообщения:stackoverflow.com/questions/37158821/… а такжеstackoverflow.com/questions/37158821/…
0

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

И это решило проблему. Я сделал несколько небольших изменений - я установил два параметра, используя System.getProperty ...

Пожалуйста, указывайте наиболее релевантную часть ссылки, если целевой сайт недоступен или постоянно недоступен.
20

эта почта с некоторыми незначительными изменениями. Это решение не требует создания каких-либо дополнительных классов.

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() );
Где определен webservicePort в приведенном выше коде (он используется в последней строке кода после приведения (BindingProvider)?
@Radek, не могли бы вы сказать, какой тип webservicePort? Похоже, что это javax.xml.ws.Service, но не уверен.
Ребята, я опубликовал это еще в 2012 году. Честно говоря, я не помню, где я определил этот порт. Извините, что это не объяснено, но у меня больше нет доступа к этому коду, чтобы на самом деле проверить и сообщить вам. Я мог бы взять его из введенного параметра и не должен был определять его сам, не уверен. Это не должно быть так сложно понять, я полагаю, если вы проведете какое-то исследование BindingProvider и его экземпляров. Если я заставлю это работать тогда, вы тоже можете. Удачи!
54

Чтобы решить эту проблему, потребовалосьKeyManager иSSLSocketFactory который использует этот обычайKeyManager чтобы получить доступ к разделеннымKeyStore. I found the base code for this KeyStore а такжеSSLFactory на этой отличной записи в блоге: как к динамически выбрать-а-сертификат псевдоним, когда-вызывающий-веб-служб

Затем специализированныеSSLSocketFactory необходимо вставить в контекст WebService:

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

ГдеgetCustomSocketFactory() возвращаетSSLSocketFactory создан с использованием метода, упомянутого выше. Это будет работать только для JAX-WS RI из встроенного в JDK пакета Sun-Oracle, учитывая, что строка, указывающаяSSLSocketFactory собственность является частной собственностью для этой реализации.

На этом этапе связь службы JAX-WS защищена через SSL, но если вы загружаете WSDL с того же защищенного сервера (), то у вас возникнет проблема с загрузкой, так как HTTPS-запрос на сбор WSDL не будет использоваться те же учетные данные, что и веб-службы. Я обошел эту проблему, сделав WSDL локально доступным (file: /// ...) и динамически изменив конечную точку веб-службы: (можно найти хорошее обсуждение того, зачем это нужно)на этом форуме)

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

Теперь WebService загружается и может обмениваться данными через SSL с аналогом сервера, используя именованный (псевдоним) Client-Certificate и взаимную аутентификацию. & # X220E;

Я добавляю новый комментарий ниже о моем временном решении.
FYI: если ваше приложение использует JAXWS-RI, вам следует использовать немного другое свойство:com.sun.xml.ws.transport.https.client.SSLSocketFactory, Я указываю как для удовлетворения различных стеков в модульном тестировании, так и в реальной среде приложения (не спрашивайте).
У нас была такая же ситуация, и в итоге мы получили копию wsdl 's локально. В противном случае у вас есть именно эта проблема начальной загрузки. maasg
На случай, если блог когда-нибудь закроется, я заархивировал его здесь:archive.is/f3goz
Это выглядит полезным только в том случае, если ваш WSDL доступен по адресу http: //, но что если он также находится по адресу https: //? Клиент терпит неудачу на первом шаге (получение WSDL).
3

вы можете получить доступ к WSDL за https:

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
Есть лиkeystore вgetResourceAsStream(keystore) ссылаетесь на путь к файлу сертификата?
не очень хорошо для меня, потому что я делаю другие звонки, которые я хочу использовать фабрику сокетов ssl по умолчанию, и это бы запутало их
В этом случае это путь внутри пути класса. Вы можете использовать File вместо getClass (). GetResourceAsStream (), если вам нужен абсолютный путь к файлу.
0

йке диспетчера доверия. Я использовал конструктор SSLContexts из apache httpclient для создания пользовательских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 метод.

Похожие вопросы