web-dev-qa-db-fra.com

Comment définir par programme le contexte SSLC d'un client JAX-WS?

Je travaille sur un serveur dans une application distribuée avec des clients de navigation et participe également à la communication de serveur à serveur avec une tierce partie . Mon serveur possède un certificat signé par une autorité de certification permettant à mes clients de se connecter à l'aide de ) communication via HTTP/S et XMPP (sécurisé). Tout fonctionne bien.

Maintenant, je dois me connecter en toute sécurité à un serveur tiers à l'aide de JAX-WS via HTTPS/SSL. Dans cette communication, mon serveur agit en tant que client dans l'interaction JAX-WS et j'ai un certificat client signé par le tiers. 

J'ai essayé d'ajouter un nouveau magasin de clés via la configuration système standard (-Djavax.net.ssl.keyStore=xyz), mais mes autres composants en sont clairement affectés. Bien que mes autres composants utilisent des paramètres dédiés pour leur configuration SSL (my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ...), il semble qu’ils finissent par utiliser la variable globale SSLContext. (L'espace de noms de configuration my.xmpp. semblait indiquer une séparation, mais ce n'est pas le cas)

J'ai également essayé d'ajouter mon certificat client dans mon magasin de clés d'origine, mais, encore une fois, mes autres composants ne semblent pas l'apprécier non plus.

Je pense qu'il ne me reste plus qu'à connecter par programme la configuration HTTPS JAX-WS pour configurer le fichier de clés et le fichier de clés certifiées pour l'interaction client JAX-WS. 

Des idées/indications sur comment faire cela? Toutes les informations que je trouve utilisent la méthode javax.net.ssl.keyStore ou définissent la variable SSLContext globale qui, je suppose, aboutira à la même configuration. Ce qui était le plus proche de quelque chose d’utile était cet ancien rapport de bogue qui demandait la fonctionnalité dont j'avais besoin: Ajout de la prise en charge pour la transmission d’un contexte SSLC au contexte d’exécution du client JAX-WS

Tout prend?

52
maasg

Celui-ci était un dur à casser, alors pour l'enregistrement:

Pour résoudre ce problème, il fallait une KeyManager personnalisée et un SSLSocketFactory qui utilise cette coutume KeyManager pour accéder à la KeyStore..__ séparée. J'ai trouvé le code de base de cette KeyStore et SSLFactory sur cette excellente entrée de blog: comment sélectionner dynamiquement un alias de certificat lors de l'appel de services Web

Ensuite, le fichier SSLSocketFactory spécialisé doit être inséré dans le contexte WebService:

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

getCustomSocketFactory() renvoie un SSLSocketFactory créé à l'aide de la méthode mentionnée ci-dessus. Cela ne fonctionnerait que pour JAX-WS RI à partir de l'implémentation Sun-Oracle intégrée dans le JDK, étant donné que la chaîne indiquant la propriété SSLSocketFactory est la propriété de cette implémentation. 

A ce stade, la communication du service JAX-WS est sécurisée via SSL. Toutefois, si vous chargez le fichier WSDL à partir du même serveur sécurisé (), vous rencontrez un problème d'amorçage, car la demande HTTPS visant à rassembler le fichier WSDL ne sera pas utilisée. les mêmes informations d'identification que le service Web. J'ai résolu ce problème en rendant le WSDL disponible localement (fichier: /// ...) et en modifiant de manière dynamique le point de terminaison du service Web: (une bonne discussion sur les raisons de cette nécessité peut être trouvée dans ce forum )

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

Maintenant, le WebService est démarré et peut communiquer via SSL avec le serveur correspondant à l'aide d'un certificat client (alias) nommé et d'une authentification mutuelle. ∎

54
maasg

Voici comment je l'ai résolu en me basant sur cet article avec quelques modifications mineures. Cette solution ne nécessite pas la création de classes supplémentaires.

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

J'ai essayé ce qui suit et cela n'a pas fonctionné sur mon environnement:

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

Mais différentes propriétés ont fonctionné comme un charme:

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

Le reste du code provient de la première réponse.

19
Igors Sakels

En combinant les réponses de Radek et de l0co, vous pouvez accéder au WSDL derrière 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
3
cidus

Ce qui précède convient (comme je l’ai dit dans un commentaire) à moins que votre WSDL ne soit accessible avec https: //.

Voici ma solution de contournement pour ceci:

Définissez SSLSocketFactory par défaut:

HttpsURLConnection.setDefaultSSLSocketFactory(...);

Pour Apache CXF que j’utilise, vous devez également ajouter ces lignes à votre configuration:

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

J'ai essayé les étapes ici:

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

Et cela a résolu le problème. J'ai fait quelques ajustements mineurs - J'ai défini les deux paramètres à l'aide de System.getProperty ...

0
Kumar Saurabh

Vous pouvez déplacer votre personnel d'authentification proxy et SSL vers un gestionnaire de savon

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

Ceci est mon exemple, faites toutes les opérations réseau

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

Il utilise UrlConnection, vous pouvez utiliser n’importe quelle bibliothèque que vous voulez dans le gestionnaire . Amusez-vous!

0
lunicon

J'ai eu des problèmes pour faire confiance à un certificat auto-signé lors de la configuration du gestionnaire de confiance. J'ai utilisé le générateur SSLContexts de Apache httpclient pour créer une variable SSLSocketFactory personnalisée.

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

et en passant la new TrustSelfSignedStrategy() en tant qu'argument dans la méthode loadTrustMaterial.

0
user3047573