2015-08-10 23 views
10

Ho cercato per giorni di farlo funzionare. Sto cercando di connettermi al mio server su https con un certificato autofirmato. Non penso che ci siano pagine o esempi che non ho ancora letto.OkHttp javax.net.ssl.SSLPeerUnverifiedException: hostname domain.com non verificato

quello che ho fatto:

  1. Creato BKS chiavi seguendo questo tutorial: http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html

utilizza openssl s_client -connect domain.com:443 per ottenere il certificato dal server. Quindi crea un keystore bks usando il castello rimbalzante.

  1. Leggere il keystore creato dalla cartella raw aggiungendolo a sslfactory e quindi a OkHttpClient. Come questo:

    public ApiService() { 
        mClient = new OkHttpClient(); 
        mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); 
        mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); 
        mClient.setCache(getCache()); 
        mClient.setCertificatePinner(getPinnedCerts()); 
        mClient.setSslSocketFactory(getSSL()); 
    } 
    
    protected SSLSocketFactory getSSL() { 
        try { 
         KeyStore trusted = KeyStore.getInstance("BKS"); 
         InputStream in = Beadict.getAppContext().getResources().openRawResource(R.raw.mytruststore); 
         trusted.load(in, "pwd".toCharArray()); 
         SSLContext sslContext = SSLContext.getInstance("TLS"); 
         TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
         trustManagerFactory.init(trusted); 
         sslContext.init(null, trustManagerFactory.getTrustManagers(), null); 
         return sslContext.getSocketFactory(); 
        } catch(Exception e) { 
         e.printStackTrace(); 
        } 
        return null; 
    } 
    
    public CertificatePinner getPinnedCerts() { 
        return new CertificatePinner.Builder() 
          .add("domain.com", "sha1/theSha=") 
          .build(); 
    } 
    
  2. Questo per qualche motivo questo genera sempre un SSLPeerUnverifiedException con o senza chiavi. E con o senza il CertificatePinner.

    javax.net.ssl.SSLPeerUnverifiedException: Hostname domain.com not verified: 0   
    W/System.err﹕ certificate: sha1/theSha= 
    W/System.err﹕ DN: 1.2.840.113549.1.9.1=#1610696e666f40626561646963742e636f6d,CN=http://domain.com,OU=development,O=domain,L=Valencia,ST=Valencia,C=ES 
    W/System.err﹕ subjectAltNames: [] 
    W/System.err﹕ at com.squareup.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:124) 
    W/System.err﹕ at com.squareup.okhttp.Connection.connect(Connection.java:143) 
    W/System.err﹕ at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:185) 
    W/System.err﹕ at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128) 
    W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:341) 
    W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:330) 
    W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:248) 
    W/System.err﹕ at com.squareup.okhttp.Call.getResponse(Call.java:273) 
    W/System.err﹕ at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230) 
    W/System.err﹕ at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201) 
    W/System.err﹕ at com.squareup.okhttp.Call.execute(Call.java:81) 
    ... 
    

Che cosa sto facendo di sbagliato?

+3

Verificare che il certificato contenga il nome host effettivo e non l'indirizzo IP (gli indirizzi IP devono essere nel campo "Nome alternativo soggetto" del certificato). Per quanto riguarda 'HostnameVerifier' che restituisce true - renderà SSL inutile e insicuro (la risposta più upvoted). Su Android androidi è possibile installare il certificato autofirmato senza problemi tramite le impostazioni di sicurezza. –

risposta

8

Finalmente ho funzionato con un mix di risposte multiple.

In primo luogo, i certificati sono stati effettuati in modo errato, non sono sicuro di come. Ma creandoli usando lo script in this answer li ha fatti funzionare. Quello che era necessario era un certificato del server e una chiave. Quindi il cliente aveva bisogno di un altro certificato.

Per utilizzare il certificato in Android ho convertito il file .pem in un file .crt come questo:

openssl x509 -outform der -in client.pem -out client.crt 

in Android ho aggiunto il certificato per il mio cliente OkHttp come la seguente:

public ApiService() { 
    mClient = new OkHttpClient(); 
    mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); 
    mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS); 
    mClient.setCache(getCache()); 
    mClient.setSslSocketFactory(getSSL()); 
} 

protected SSLSocketFactory getSSL() { 
    try { 
     CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
     InputStream cert = getAppContext().getResources().openRawResource(R.raw.client); 
     Certificate ca = cf.generateCertificate(cert); 
     cert.close(); 

     // creating a KeyStore containing our trusted CAs 
     String keyStoreType = KeyStore.getDefaultType(); 
     KeyStore keyStore = KeyStore.getInstance(keyStoreType); 
     keyStore.load(null, null); 
     keyStore.setCertificateEntry("ca", ca); 

     return new AdditionalKeyStore(keyStore); 
    } catch(Exception e) { 
     e.printStackTrace(); 
    } 
    return null; 
} 

L'ultima parte con new AdditionalKeyStore() viene presa da this very well written answer. Che aggiunge un keystore di fallback.

Spero che questo possa aiutare qualcun altro! Questo è il modo più semplice per far funzionare HTTPS con un certificato autofirmato che ho trovato. Altri modi includono avere un keystore BouncyCastle che mi sembra eccessivo.

+0

qual è il file (client) in raw? – JUL2791

+0

che è il file 'client.crt' generato nella riga openssl. –

+0

Qual è il file client.pem che hai usato qui? Quello script genera pochi file .pem. È il file chain.pem nella directory del client? – Chrishan

15

Ho avuto lo stesso problema, tuttavia avevo bisogno che la mia applicazione funzionasse su diversi ambienti di staging, tutti dotati di certificati autofirmati. A peggiorare le cose, potevano cambiare quei certificati al volo.

Per risolvere questo problema, quando si collegava solo alla gestione temporanea, ho aggiunto un SSLSocketFactory che si fidava di tutti i certificati. Questo ha risolto l'errore java, tuttavia mi ha lasciato l'eccezione okhttp annotata in questo ticket.

Per evitare questo errore, ho dovuto aggiungere un'altra personalizzazione al mio okHttpClient. Questo ha corretto l'errore per me.

okHttpClient.setHostnameVerifier(new HostnameVerifier() { 
      @Override 
      public boolean verify(String hostname, SSLSession session) { 
       return true; 
      } 
     }); 
+5

Questo non è sicuro ! So che non l'ho specificato nella mia domanda. So che funziona, l'ho usato prima. Ma ora sono cresciuto e voglio la piena sicurezza di https. :) –

+0

ovviamente questo non è sicuro - questo esclude esplicitamente la verifica del certificato https. Questo dovrebbe essere usato solo in ambienti pre-produzione in cui si desidera evitare la verifica SSL, mai su una build di produzione. –

+0

'setHostnameVerifier' non sta compilando –

6

Questo problema è risolto modificando setHostNameVerifier a okHttpBuilder. Assicurati che il metodo di verifica debba restituire true.

Esempio:

okHttpClient.setHostnameVerifier(new HostnameVerifier() { 
    @Override 
    public boolean verify(String hostname, SSLSession session) { 
     return true; 
    } 
}); 

OkHttpClient.Builder builder = new OkHttpClient.Builder(); 
    builder.hostnameVerifier(new HostnameVerifier() { 
     @Override 
     public boolean verify(String hostname, SSLSession session) { 
      return true; 
     } 
    }); 
OkHttpClient client = builder.build(); 
+0

Quanto è sicuro? Se restituisce true, non tutti i certificati sono accettati? Se è così che dovrebbe essere evitato! –

+0

Sono d'accordo @just_user. Funziona, ma non è una buona soluzione. Destra...? – Otziii

+0

@Otziii Sì, questo non è sicuro. Non so perché la gente continua a suggerire questa "soluzione". Controlla la mia risposta sopra, questo offre un'opzione più sicura. –

0

Si prega di verificare se il nome CN sul cert client viene aggiunto al nome alternativo del soggetto. Ho avuto lo stesso problema