web-dev-qa-db-fra.com

OkHttp javax.net.ssl.SSLPeerUnverifiedException: nom d'hôte domain.com non vérifié

J'essaye depuis des jours de faire fonctionner ça. J'essaie de me connecter à mon serveur via https avec un certificat auto-signé. Je ne pense pas qu'il y ait de pages ou d'exemples que je n'ai pas encore lus.

Ce que j'ai fait:

  1. Création du magasin de clés bks en suivant ce tutoriel: http://blog.crazybob.org/2010/02/Android-trusting-ssl-certificates.html

Il utilise openssl s_client -connect domain.com:443 pour obtenir le certificat du serveur. Crée ensuite un magasin de clés bks à l'aide d'un château gonflable.

  1. Lecture du fichier de clés créé à partir du dossier brut en l'ajoutant à sslfactory, puis à OkHttpClient. Comme ça:

    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. Cela pour une raison quelconque, cela génère toujours un SSLPeerUnverifiedException avec ou sans le magasin de clés. Et avec ou sans 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)
     ...
    

Qu'est-ce que je fais mal?

17
just_user

J'ai finalement réussi à travailler avec un mélange de réponses multiples.

Premièrement, les certificats ont été faits à tort, je ne sais pas comment. Mais en les créant en utilisant le script dans cette réponse les a fait fonctionner. Ce qu'il fallait, c'était un certificat de serveur et une clé. Ensuite, le client avait besoin d'un autre certificat.

Pour utiliser le certificat dans Android j'ai converti le fichier .pem en un fichier .crt comme ceci:

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

Dans Android j'ai ajouté le certificat à mon client OkHttp comme suit:

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

La dernière partie avec new AdditionalKeyStore() est tirée de cette réponse très bien écrite . Ce qui ajoute un magasin de clés de secours.

J'espère que cela pourrait aider quelqu'un d'autre! C'est le moyen le plus simple de faire fonctionner HTTPS avec un certificat auto-signé que j'ai trouvé. D'autres moyens incluent la possession d'un magasin de clés BouncyCastle qui me semble excessif.

12
just_user

J'ai eu le même problème, mais j'avais besoin que mon application fonctionne sur plusieurs environnements de transfert, qui avaient tous des certificats auto-signés. Pour aggraver les choses, ils pourraient changer ces certificats à la volée.

Pour résoudre ce problème, lors de la connexion au transfert uniquement, j'ai ajouté un SSLSocketFactory qui approuvait tous les certificats. Cela a corrigé l'erreur Java, mais cela m'a laissé l'exception okhttp notée dans ce ticket.

Pour éviter cette erreur, j'ai dû ajouter une personnalisation supplémentaire à mon okHttpClient. Cela a corrigé l'erreur pour moi.

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

Ce problème est résolu en définissant setHostNameVerifier sur okHttpBuilder. Assurez-vous que la méthode de vérification doit renvoyer true.

Échantillon:

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

Veuillez vérifier si le nom CN sur le certificat client est ajouté à l'autre nom du sujet. J'ai eu le même problème

3
Rahul Goel