web-dev-qa-db-de.com

Wie programmiere ich den SSLContext eines JAX-WS-Clients programmgesteuert?

Ich arbeite auf einem Server in einer verteilten Anwendung, die über Browser-Clients verfügt und auch an einer Server-zu-Server-Kommunikation mit einem Drittanbieter beteiligt ist .. __ Mein Server verfügt über ein von einer Zertifizierungsstelle signiertes Zertifikat, über das meine Clients mithilfe von TLS (SSL) eine Verbindung herstellen können ) Kommunikation über HTTP/S und XMPP (sicher). Das funktioniert alles gut.

Jetzt muss ich mich mit JAX-WS über HTTPS/SSL sicher mit einem Drittanbieter-Server verbinden. In dieser Kommunikation fungiert mein Server als Client in der JAX-WS-Interation, und ich habe ein Clientzertifikat, das vom Dritten unterzeichnet wurde. 

Ich habe versucht, einen neuen Keystore über die Standardsystemkonfiguration (-Djavax.net.ssl.keyStore=xyz) hinzuzufügen, aber meine anderen Komponenten sind davon eindeutig betroffen. Obwohl meine anderen Komponenten dedizierte Parameter für ihre SSL-Konfiguration verwenden (my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...), verwenden sie anscheinend die globale Variable SSLContext. (Der Konfigurations-Namespace my.xmpp. schien eine Trennung anzuzeigen, ist aber nicht der Fall.)

Ich habe auch versucht, mein Clientzertifikat in meinen ursprünglichen Keystore einzufügen, aber auch meine anderen Komponenten scheinen es nicht zu mögen.

Ich denke, meine einzige Option ist das programmatische Einbinden in die JAX-WS HTTPS-Konfiguration, um den Keystore und den Truststore für die Client-JAX-WS-Interaktion einzurichten. 

Irgendwelche Ideen/Hinweise, wie das geht? Alle Informationen, die ich finde, verwenden entweder die javax.net.ssl.keyStore-Methode oder stellen die globale SSLContext ein, mit der -Ich schätze- in derselben Konfidenz endet. Am hilfreichsten war mir dieser alte Fehlerbericht, der die benötigte Funktion anforderte: Unterstützung für die Weitergabe eines SSLContextes an die JAX-WS-Clientlaufzeit hinzufügen

Irgendwelche nimmt?

52
maasg

Dieser war eine harte Nuss zu knacken, also für das Protokoll:

Um dies zu lösen, waren ein benutzerdefiniertes KeyManager und ein SSLSocketFactory erforderlich, die dieses benutzerdefinierte KeyManager verwenden, um auf das getrennte KeyStore zuzugreifen. Ich habe den Basiscode für diese KeyStore und SSLFactory in diesem hervorragenden Blogeintrag gefunden: wie-ein-Zertifikat-Alias-beim-Aufrufen-von-Webservices-dynamisch-ausgewählt wird

Dann muss das spezialisierte SSLSocketFactory in den WebService-Kontext eingefügt werden:

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

Wobei getCustomSocketFactory() ein SSLSocketFactory zurückgibt, das mit der oben genannten Methode erstellt wurde. Dies funktioniert nur für JAX-WS RI von dem in das JDK integrierten Sun-Oracle-Impl, da die Zeichenfolge, die die Eigenschaft SSLSocketFactory angibt, für diese Implementierung proprietär ist.

Zu diesem Zeitpunkt ist die JAX-WS-Dienstkommunikation über SSL gesichert. Wenn Sie die WSDL jedoch von demselben sicheren Server () laden, tritt das Problem bootstrap auf, wie bei der HTTPS-Anforderung zum Sammeln der WSDL verwendet nicht dieselben Anmeldeinformationen wie der Webdienst. Ich habe dieses Problem umgangen, indem ich die WSDL lokal verfügbar gemacht habe (Datei: /// ...) und den Webdienst-Endpunkt dynamisch geändert habe: (eine gute Diskussion darüber, warum dies erforderlich ist, finden Sie in diesem Forum =)

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

Jetzt wird der WebService gebootet und kann über SSL mit dem Server-Gegenstück unter Verwendung eines benannten (Alias-) Client-Zertifikats und gegenseitiger Authentifizierung kommunizieren. ∎

54
maasg

So löste ich es basierend auf diesem Beitrag mit einigen kleinen Änderungen. Diese Lösung erfordert keine zusätzlichen Klassen.

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() );
20
Radek

Ich habe folgendes versucht und in meiner Umgebung hat es nicht funktioniert:

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

Aber ein anderes Objekt funktionierte wie ein Zauber:

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

Der Rest des Codes wurde aus der ersten Antwort übernommen.

19
Igors Sakels

Durch die Kombination der Antworten von Radek und l0co können Sie auf die WSDL hinter https zugreifen:

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
3
cidus

Das obige ist in Ordnung (wie gesagt in Kommentar), es sei denn, Ihre WSDL ist auch mit https: // erreichbar.

Hier ist meine Abhilfe dafür:

Legen Sie Ihre SSLSocketFactory als Standard fest:

HttpsURLConnection.setDefaultSSLSocketFactory(...);

Für Apache CXF, den ich verwende, müssen Sie Ihrer Konfiguration auch folgende Zeilen hinzufügen:

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

Für diejenigen, die es versuchen und immer noch nicht zum Laufen bringen, hat dies mit Wildfly 8 funktioniert, wobei das dynamische Dispatcher verwendet wurde:

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

Beachten Sie, dass der Teil internal aus dem Eigenschaftsschlüssel hier weg ist.

0
Davio

Ich habe die Schritte hier versucht:

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

Und das Problem wurde behoben. Ich habe ein paar kleinere Verbesserungen vorgenommen - ich habe die beiden Parameter mit System.getProperty eingestellt ...

0
Kumar Saurabh

Sie können Ihre Proxy-Authentifizierung und Ihr SSL-Personal zum Soap-Handler verschieben

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

Dies ist mein Beispiel, alle Netzwerkoperationen ausführen

  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();
    }
  }

Es kann UrlConnection verwenden, Sie können jede Bibliothek verwenden, die Sie im Handler benötigen .. _. Viel Spaß!

0
lunicon

Beim Einrichten des Trust-Managers hatte ich Probleme, einem selbstsignierten Zertifikat zu vertrauen. Ich habe den Builder für SSLContexts des Apache-HTTP-Clients verwendet, um eine benutzerdefinierte SSLSocketFactory zu erstellen

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);

und Übergabe der new TrustSelfSignedStrategy() als Argument in der loadTrustMaterial-Methode.

0
user3047573