OkHttp javax.net.ssl.SSLPeerUnverifiedException: имя хоста domain.com не проверено

Я пробовал в течение нескольких дней, чтобы это работало. Я пытаюсь подключиться к моему серверу через https с самоподписанным сертификатом. Я не думаю, что есть какие-то страницы или примеры, которые я еще не читал.

Что я наделал:

  1. Создал bes keystore, следуя этому руководству: http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html

Он использует openssl s_client -connect domain.com:443 для получения сертификата с сервера. Затем создает bes keystore, используя надувной замок.

  1. Чтение создало хранилище ключей из необработанной папки, добавив его в sslfactory, а затем в OkHttpClient. Как это:

     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. Это почему-то всегда генерирует SSLPeerUnverifiedException с хранилищем ключей или без него. И с или без 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) ... 

Что я делаю не так?

У меня была та же проблема, однако мне было нужно приложение для работы в нескольких промежуточных средах, все из которых имели собственные сертификаты. Хуже того, они могут менять эти сертификаты «на лету».

Чтобы исправить это, при подключении только к промежуточной стадии я добавил SSLSocketFactory, который доверял всем сертификатам. Это зафиксировало java-ошибку, однако она оставила меня с исключением okhttp, отмеченным в этом билете.

Чтобы избежать этой ошибки, мне нужно было добавить еще одну настройку в мой okHttpClient. Это фиксировало ошибку для меня.

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

Я, наконец, получил эту работу с несколькими ответами.

Во-первых, сертификаты были сделаны ошибочно, не знаю, как это сделать. Но, создав их, используя сценарий в этом ответе, они заставили их работать. Нужен был сертификат сервера и ключ. Затем клиенту понадобился другой сертификат.

Чтобы использовать сертификат в android, я преобразовал файл .pem в файл .crt следующим образом:

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

В android я добавил сертификат к моему клиенту OkHttp, как показано ниже:

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

Последняя часть с new AdditionalKeyStore() взята из этого очень хорошо написанного ответа . Что добавляет резервное хранилище ключей.

Надеюсь, это поможет кому-нибудь еще! Это самый простой способ заставить HTTPS работать с самозаверяющим сертификатом, который я нашел. Другие способы включают наличие хранилища BouncyCastle, которое кажется чрезмерным для меня.

Эта проблема решается установкой setHostNameVerifier в okHttpBuilder . Убедитесь, что метод проверки возвращает true.

Образец:

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