Come impostare a livello di codice SSLContext di un client JAX-WS?

Sto lavorando su un server in un’applicazione distribuita che dispone di client browser e partecipa anche a comunicazioni server-server con terze parti. Il mio server ha un certificato firmato da una CA che consente ai miei clienti di connettersi utilizzando la comunicazione TLS (SSL) utilizzando HTTP / S e XMPP (sicuro). Funziona tutto bene.

Ora devo connettermi in modo sicuro a un server di terze parti utilizzando JAX-WS su HTTPS / SSL. In questa comunicazione, il mio server funge da client nell’interazione JAX-WS e ho un certificato client firmato dalla terza parte.

Ho provato ad aggiungere un nuovo keystore attraverso la configurazione di sistema standard ( -Djavax.net.ssl.keyStore=xyz ) ma i miei altri componenti sono chiaramente interessati da questo. Sebbene i miei altri componenti stiano utilizzando parametri dedicati per la loro configurazione SSL ( my.xmpp.keystore=xxx, my.xmpp.truststore=xxy, ... ), sembra che finiscano per utilizzare il SSLContext globale. (Lo spazio my.xmpp. nomi di configurazione my.xmpp. sembrava indicare una separazione, ma non è il caso)

Ho anche provato ad aggiungere il mio certificato client nel mio keystore originale, ma – va bene – anche i miei altri componenti non sembrano graditi.

Penso che la mia unica opzione rimasta sia quella di aghook programmatico alla configurazione HTTPS JAX-WS per configurare il keystore e il truststore per l’interazione client JAX-WS.

Qualche idea / suggerimenti su come farlo? Tutte le informazioni che trovo utilizzano il metodo javax.net.ssl.keyStore o impostano il SSLContext globale che -I indovina- finirà nella stessa configurazione. Il più vicino a qualcosa di utile è stato questo vecchio bug report che richiede la funzionalità di cui ho bisogno: aggiungere il supporto per passare un SSLContext al runtime del client JAX-WS

Qualche ciak?

Questo era difficile da decifrare, quindi per la cronaca:

Per risolvere questo problema, è richiesto un KeyManager personalizzato e un SSLSocketFactory che utilizza questo KeyManager personalizzato per accedere al KeyStore separato. Ho trovato il codice base per questo KeyStore e SSLFactory su questo eccellente post di blog: how-to-dynamically-select-a-certificate-alias-when-invoking-web-services

Quindi, lo specifico SSLSocketFactory deve essere inserito nel contesto WebService:

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

Dove getCustomSocketFactory() restituisce un SSLSocketFactory creato utilizzando il metodo menzionato sopra. Ciò funzionerebbe solo per JAX-WS RI dall’impianto Sun-Oracle integrato nel JDK, dato che la stringa che indica la proprietà SSLSocketFactory è proprietaria per questa implementazione.

In questa fase, la comunicazione del servizio JAX-WS è protetta tramite SSL, ma se si sta caricando il WSDL dallo stesso server sicuro () si avrà un problema di bootstrap, poiché la richiesta HTTPS per raccogliere il WSDL non verrà utilizzata le stesse credenziali del servizio Web. Ho risolto questo problema rendendo disponibile WSDL localmente (file: /// …) e cambiando dynamicmente l’endpoint del servizio Web: (una buona discussione sul perché è necessario è disponibile in questo forum )

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

A questo punto, il servizio Web viene riavviato ed è in grado di comunicare tramite SSL con la controparte del server utilizzando un certificato client denominato (alias) e l’autenticazione reciproca. ∎

Questo è il modo in cui l’ho risolto in base a questo post con alcune modifiche minori. Questa soluzione non richiede la creazione di alcuna class aggiuntiva.

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

Ho provato quanto segue e non ha funzionato sul mio ambiente:

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

Ma la proprietà diversa ha funzionato come un fascino:

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

Il resto del codice è stato preso dalla prima risposta.

Combinando le risposte di Radek e di l0co puoi accedere al WSDL dietro 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 

Quanto sopra va bene (come ho detto nel commento) a meno che il tuo WSDL sia accessibile anche con https: //.

Ecco la mia soluzione per questo:

Imposta SSLSocketFactory come predefinito:

 HttpsURLConnection.setDefaultSSLSocketFactory(...); 

Per Apache CXF che utilizzo è necessario aggiungere anche queste righe alla configurazione:

    

Per coloro che cercano e ancora non riescono a farlo funzionare, questo lo ha fatto per me con Wildfly 8, usando il Dispatcher dinamico:

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

Si noti che la parte internal della chiave Proprietà è stata cancellata qui.

Ho avuto problemi a fidarsi di un certificato autofirmato quando ho configurato il gestore di fiducia. Ho usato il builder SSLContexts dell’apache httpclient per creare un SSLSocketFactory personalizzato

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

e passando il new TrustSelfSignedStrategy() come argomento nel metodo loadTrustMaterial .

Ho provato i passaggi qui:

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

E questo ha risolto il problema. Ho apportato alcune modifiche minori: ho impostato i due parametri usando System.getProperty …