2010-09-14 23 views
24

Il nostro sistema comunica con diversi provider di servizi Web. Sono tutti invocati da una singola applicazione client Java. Tutti i servizi Web fino ad ora sono stati su SSL, ma nessuno usa i certificati client. Bene, un nuovo partner sta cambiando.Scelta del certificato client SSL in Java

Fare in modo che l'applicazione utilizzi un certificato per l'invocazione è semplice; l'impostazione javax.net.ssl.keyStore e javax.net.ssl.keyStorePassword lo farà. Tuttavia, il problema è ora come farlo in modo che utilizzi il certificato solo quando si richiama quel particolare servizio web. Immagino più in generale, vorremmo poter scegliere il certificato client da utilizzare, se esiste.

Una soluzione rapida potrebbe essere impostare le proprietà del sistema, richiamare i metodi e quindi disattivarli. L'unico problema è che abbiamo a che fare con un'applicazione multi-thread, quindi ora avremmo bisogno di gestire la sincronizzazione o i blocchi o cosa avete.

Ogni client di servizio dovrebbe essere completamente indipendente l'uno dall'altro e sono confezionati singolarmente in JAR separati. Quindi, un'opzione che mi è venuta in mente (anche se non l'abbiamo analizzata correttamente) è di isolare in qualche modo ogni JAR, magari caricandoli ciascuno sotto una VM diversa con parametri diversi. È semplicemente un'idea che non so come implementare (o se è addirittura possibile, per quello).

This post suggerisce che è possibile selezionare un singolo certificato da un keystore, ma come collegarlo alla richiesta sembra essere un problema diverso del tutto.

Stiamo utilizzando le classi Java 1.5, Axis2 e client generate con wsimport o wsdl2java.

risposta

7

I client SSL Java invieranno un certificato solo se richiesto dal server. Un server può inviare un suggerimento opzionale su quali certificati accetterà; questo aiuterà un cliente a scegliere un singolo certificato se ha più di uno.

Normalmente, un nuovo SSLContext viene creato con un certificato client specifico e le istanze Socket vengono create da una fabbrica ottenuta da tale contesto. Sfortunatamente, Axis2 non sembra supportare l'uso di uno SSLContext o di uno personalizzato SocketFactory. Le sue impostazioni del certificato client sono globali.

+0

Purtroppo non abbiamo alcun controllo sui server. Speravo anche che questi suggerimenti potessero essere utili, ma un test ha dimostrato che almeno con un server non lo erano. WRT Axis2, è stata anche la mia esperienza. Se ti capita di conoscere un altro strumento Java che consente questo tipo di comportamento, apprezzerei molto la tua condivisione; lo esploreremo volentieri – Carlos

+0

@Carlos - Forse non ero chiaro. Non hai bisogno di alcun controllo sul server; se il tuo partner richiede ora un certificato client, configurerà il suo server per richiederlo. Il tuo cliente * non * invierà quel certificato a nessun altro server, perché (presumibilmente) non lo richiederanno. L'unico problema potenziale potrebbe essere se i servizi aggiuntivi iniziano a richiedere un certificato client, ma non accettano le stesse CA. – erickson

+0

Capito, grazie. Immagino che l'altra domanda resti, però.Se domani aggiungiamo un altro servizio Web che richiede anche un certificato client, esiste un modo per scegliere quale certificato nel keystore presentare a ciascun servizio? – Carlos

14

La configurazione viene eseguita tramite SSLContext, che è effettivamente una fabbrica per SSLSocketFactory (o SSLEngine). Per impostazione predefinita, questo sarà configurato dalle proprietà javax.net.ssl.*. Inoltre, quando un server richiede un certificato, invia un messaggio TLS/SSL CertificateRequest che contiene un elenco di nomi distinti di CA che è disposto ad accettare. Sebbene questo elenco sia strettamente indicativo (cioè i server potrebbero accettare certificati di emittenti non presenti nell'elenco o rifiutare certificati validi da CA nell'elenco), di solito funziona in questo modo.

Per impostazione predefinita, il selettore di certificati nello X509KeyManager configurato all'interno dello SSLContext (di nuovo normalmente non si deve preoccupare di esso), sceglierà uno dei certificati che è stato emesso da uno nell'elenco (o può essere incatenato a un emittente lì). Tale elenco è il parametro issuers in X509KeyManager.chooseClientAlias (lo alias è il nome alias per il certificato che si desidera selezionare, come indicato all'interno del keystore). Se si dispone di più candidati, è anche possibile utilizzare il parametro socket, che consente di ottenere l'indirizzo IP del peer se questo consente di effettuare una scelta.

Se questo aiuta, è possibile trovare utilizzando jSSLutils (and its wrapper) per la configurazione del tuo SSLContext (queste sono principalmente classi di supporto per creare SSLContext s). (Si noti che questo esempio è per la scelta del alias lato server, ma può essere adattato, il source code is available.)

Una volta fatto questo, si dovrebbe cercare la documentazione relativa alla proprietà di sistema axis.socketSecureFactory nell'Asse (e SecureSocketFactory). Se si guarda il codice sorgente dell'Asse, non dovrebbe essere troppo difficile creare un org.apache.axis.components.net.SunJSSESocketFactory inizializzato dallo SSLContext di propria scelta (vedere this question).

Ho appena realizzato che stavi parlando di Axis2, dove lo SecureSocketFactory sembra essere scomparso. Potrebbe essere possibile trovare una soluzione alternativa utilizzando il valore predefinito SSLContext, ma ciò influirà sull'intera applicazione (che non è eccezionale). Se si utilizza uno X509KeyManagerWrapper di jSSLutils, è possibile utilizzare lo X509KeyManager predefinito e considerare solo alcuni host come eccezione. (Questa non è una situazione ideale, non sono sicuro di come utilizzare una consuetudine SSLContext/SSLSocketFactory in Asse 2.)

In alternativa, secondo this Axis 2 document, sembra Asse 2 utilizza 3.x client Apache HTTP:

Se si desidera eseguire l'autenticazione client SSL (2 vie SSL), è possibile utilizzare la funzione di Protocol.registerProtocol di HttpClient. È possibile sovrascrivere il protocollo "https" oppure utilizzare un protocollo diverso per le comunicazioni di autenticazione del client SSL se non si desidera interferire con i normali https . Per ulteriori informazioni: http://jakarta.apache.org/commons/httpclient/sslguide.html

In questo caso, il SslContextedSecureProtocolSocketFactory dovrebbe aiutare si configura un SSLContext.

+0

Hum ... e hai appena realizzato che stai usando Java 1.5, quindi non 'SSLContext.setDefault (...)'. Perché non usare Java 6? Java 5 non è più supportato per quanto ne so (non avrebbe alcun danno avere diverse patch di sicurezza). – Bruno

+0

(L'impostazione di 'SSLContext' tramite Apache HTTP Client 3.x dovrebbe funzionare con Java 1.5.) – Bruno

+0

Grazie per il vostro aiuto! – Carlos

0

ho inizializzato EasySSLProtocolSocketFactory e protocollo casi per i diversi endpoint e registrare il protocollo con la chiave univoca in questo modo:

/** 
* This method does the following: 
* 1. Creates a new and unique protocol for each SSL URL that is secured by client certificate 
* 2. Bind keyStore related information to this protocol 
* 3. Registers it with HTTP Protocol object 
* 4. Stores the local reference for this custom protocol for use during furture collect calls 
* 
* @throws Exception 
*/ 
public void registerProtocolCertificate() throws Exception { 
    EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory(); 
    easySSLPSFactory.setKeyMaterial(createKeyMaterial()); 
    myProtocolPrefix = (HTTPS_PROTOCOL + uniqueCounter.incrementAndGet()); 
    Protocol httpsProtocol = new Protocol(myProtocolPrefix,(ProtocolSocketFactory) easySSLPSFactory, port); 
    Protocol.registerProtocol(myProtocolPrefix, httpsProtocol); 
    log.trace("Protocol [ "+myProtocolPrefix+" ] registered for the first time"); 
} 

/** 
* Load keystore for CLIENT-CERT protected endpoints 
*/ 
private KeyMaterial createKeyMaterial() throws GeneralSecurityException, Exception { 
    KeyMaterial km = null; 
    char[] password = keyStorePassphrase.toCharArray(); 
    File f = new File(keyStoreLocation); 
    if (f.exists()) { 
     try { 
      km = new KeyMaterial(keyStoreLocation, password); 
      log.trace("Keystore location is: " + keyStoreLocation + ""); 
     } catch (GeneralSecurityException gse) { 
      if (logErrors){ 
       log.error("Exception occured while loading keystore from the following location: "+keyStoreLocation, gse); 
       throw gse; 
      } 
     } 
    } else { 
     log.error("Unable to load Keystore from the following location: " + keyStoreLocation); 
     throw new CollectorInitException("Unable to load Keystore from the following location: " + keyStoreLocation); 
    } 
    return km; 
} 

Quando devo richiamare il servizio web, faccio questo (che in pratica sostituire "https" in l'URL con https1, o https2 o qualcos'altro a seconda del protocollo che si inizializzata per quel particolare punto finale):

httpClient.getHostConfiguration().setHost(host, port,Protocol.getProtocol(myProtocolPrefix)); 
initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix)); 

funziona come un fascino!

+1

Puoi indicare le importazioni? EasySSLProtocolSocketFactory? Keymaterial? Un esempio completo di dipendenze sarebbe molto bello. Grazie. – marcolopes

+0

Sarebbe bello se potessi, per favore, approfondire il tuo codice un po 'di più. –

+0

Dai un'occhiata a questo file su github per rispondere alla maggior parte delle tue domande: https://github.com/nickman/helios/blob/gh-pages/helios-collectors/collectors-http/src/main/java/org/ helios/collezionisti/url/URLCollector.java – helios

Problemi correlati