Apache HTTPClient SSLPeerUnverifiedException

Utilizzo di Apache HttpClient 4.2.1. Utilizzo del codice copiato dall’esempio di accesso basato su modulo

http://hc.apache.org/httpcomponents-client-ga/examples.html

Ricevo un’eccezione quando accedo a un modulo di accesso protetto da SSL:

Getting library items from https://appserver.gtportalbase.com/agileBase/AppController.servlet?return=blank javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated Closing http connection at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180) at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:294) at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:640) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:479) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784) 

Il certificato per quanto posso dire va bene (vedi l’URL prima della traccia dello stack), non è scaduto – i browser non si lamentano.

Ho provato a importare il certificato nel mio keystore a la

Come gestire i certificati SSL non validi con Apache HttpClient?

senza alcun cambiamento. Credo che sia ansible creare un SSLContext personalizzato per forzare Java a ignorare l’errore, ma preferirei risolvere la causa principale in quanto non voglio aprire buchi di sicurezza.

Qualche idea?

EDIT Mi rendo conto che questa risposta è stata accettata molto tempo fa ed è stata anche upvoted per 3 volte, ma è stata (almeno in parte) non corretta, quindi ecco un po ‘di più su questa eccezione. Scuse per l’inconveniente.

javax.net.ssl.SSLPeerUnverifiedException: peer non autenticato

Solitamente si tratta di un’eccezione generata quando il server remoto non ha inviato alcun certificato. Tuttavia, vi è un caso limite, che si verifica quando si utilizza Apache HTTP Client, a causa del modo in cui è stato implementato in questa versione, e per via del modo sun.security. ssl.SSLSocketImpl.getSession() sun.security. ssl.SSLSocketImpl.getSession() è implementato.

Quando si utilizza Apache HTTP Client, questa eccezione verrà generata anche quando il certificato remoto non è attendibile, il che comporterebbe più spesso ” sun.security.validator.ValidatorException: PKIX path building failed “.

La ragione per cui ciò accade è perché il client HTTP Apache tenta di ottenere SSLSession e il certificato peer prima di fare qualsiasi altra cosa.

Come promemoria, ci sono 3 modi per avviare l’handshake con un SSLSocket :

  • chiamando startHandshake che inizia esplicitamente le strette di mano, o
  • qualsiasi tentativo di leggere o scrivere i dati dell’applicazione su questo socket provoca un handshake implicito, oppure
  • una chiamata a getSession tenta di impostare una sessione se non esiste una sessione valida al momento e viene eseguito un handshake implicito.

Qui ci sono 3 esempi, tutti contro un host con un certificato che non è attendibile (usando javax.net.ssl.SSLSocketFactory , non quello di Apache).

Esempio 1:

  SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 443); sslSocket.startHandshake(); 

Questo genera ” javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed ” (come previsto).

Esempio 2:

  SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 443); sslSocket.getInputStream().read(); 

Ciò inoltre genera ” javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed ” (come previsto).

Esempio 3:

  SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 443); SSLSession sslSession = sslSocket.getSession(); sslSession.getPeerCertificates(); 

Questo, tuttavia, genera javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated .

Questa è la logica implementata in AbstractVerifier del client HTTP Apache utilizzato dal suo org.apache.http.conn.ssl.SSLSocketFactory nella versione 4.2.1. Versioni successive effettuano una chiamata esplicita a startHandshake() , in base ai report in questione HTTPCLIENT-1346 .

Questo alla fine sembra provenire dall’implementazione di sun.security. ssl.SSLSocketImpl.getSession() sun.security. ssl.SSLSocketImpl.getSession() , che sun.security. ssl.SSLSocketImpl.getSession() potenziali IOException s generate quando chiamano startHandshake(false) (metodo interno), senza lanciare ulteriormente. Questo potrebbe essere un bug, anche se questo non dovrebbe avere un enorme impatto sulla sicurezza, dal momento che SSLSocket sarà comunque chiuso.

Esempio 4:

  SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example", 443); SSLSession sslSession = sslSocket.getSession(); // sslSession.getPeerCertificates(); sslSocket.getInputStream().read(); 

Fortunatamente, questo ancora getterà ” javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed “, ogni volta che si tenta effettivamente di utilizzare tale SSLSocket (non ci sono scappatoie ottenendo la sessione senza ottenere il certificato peer ).


Come risolvere questo

Come qualsiasi altro problema con certificati che non sono attendibili, è una questione di assicurarsi che il trust store che stai utilizzando contenga i necessari ancore di fiducia (cioè i certificati CA che hanno emesso la catena che stai cercando di verificare, o probabilmente il server vero e proprio certificato per casi eccezionali).

Per risolvere questo problema, è necessario importare il certificato CA (o eventualmente il certificato stesso del server) nel proprio archivio sicuro. Puoi farlo:

  • nel proprio archivio di fiducia JRE, in genere il file cacerts (che non è necessariamente il migliore, perché ciò influirebbe su tutte le applicazioni che utilizzano quel JRE),
  • in una copia locale del tuo negozio di fiducia (che puoi configurare usando le opzioni -Djavax.net.ssl.trustStore=... ),
  • creando uno specifico SSLContext per quella connessione (come descritto in questa risposta ). (Alcuni suggeriscono di usare un gestore di fiducia che non fa nulla, ma questo renderebbe la tua connessione vulnerabile agli attacchi MITM.)

Risposta iniziale

javax.net.ssl.SSLPeerUnverifiedException: peer non autenticato

Questo non ha nulla a che fare con i certificati SSLContext o con la creazione di un SSLContext personalizzato: questo è dovuto al fatto che il server non sta inviando alcun certificato.

Questo server è visibilmente non configurato per supportare correttamente TLS. Questo fallisce (non otterrai un certificato remoto):

 openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443 

Tuttavia, SSLv3 sembra funzionare:

 openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443 

Se sai chi sta gestendo questo server, varrebbe la pena contattarlo per risolvere questo problema. I server dovrebbero davvero supportare TLSv1 almeno al giorno d’oggi.

Nel frattempo, un modo per risolvere questo problema sarebbe creare il proprio org.apache.http.conn.ssl.SSLSocketFactory e utilizzarlo per questa connessione con il client Http Apache.

Questo factory dovrebbe creare un SSLSocket come al solito, usa sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); prima di restituire quel socket, per disabilitare TLS, che altrimenti sarebbe abilitato di default.

Creare un contesto personalizzato in modo da poter registrare il motivo per cui il certificato non è valido. O semplicemente fai il debug in esso.