Поддерживает ли OkHttp прием самозаверяющих сертификатов SSL?
Я работаю для клиента, у которого есть сервер с самозаверяющим сертификатом SSL.
Я использую Retrofit + CustomClient с помощью обернутого клиента OkHttp:
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(Config.BASE_URL + Config.API_VERSION)
.setClient(new CustomClient(new OkClient(), context))
.build();
Не OkHttp поддерживает вызов самозаверяющие SSL-сертификат сервера по умолчанию?
кстати. Какой клиент использует Retrofit по умолчанию? Я думал, что это был OkHttp, но когда я исследовал немного больше, я понял, что мне необходимо импортировать зависимостей OkHttp
5 ответов:
Да, это делает.
Retrofit позволяет настроить пользовательский HTTP-клиент, который настроен для ваших нужд.
что касается самоподписанных SSL-сертификатов, есть обсуждение здесь. Ссылка содержит примеры кода для добавления self signed SLL в Android
DefaultHttpClientи загрузить этот клиент для Ретрофита.Если вам нужно
OkHttpClientчтобы принять самозаверяющий SSL, вам нужно передать его customjavax.net.ssl.SSLSocketFactoryчерезsetSslSocketFactory(SSLSocketFactory sslSocketFactory)метод.в самый простой способ получить фабрику сокетов-получить один из
javax.net.ssl.SSLContextкак обсуждалось здесь.вот пример настройки OkHttpClient:
OkHttpClient client = new OkHttpClient(); KeyStore keyStore = readKeyStore(); //your method to obtain KeyStore SSLContext sslContext = SSLContext.getInstance("SSL"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, "keystore_pass".toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(), new SecureRandom()); client.setSslSocketFactory(sslContext.getSocketFactory());
обновленный код для okhttp3 (с помощью builder):
OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory()) .build();
the
clientздесь теперь настроен для использования сертификатов от вашегоKeyStore. Однако он будет доверять только сертификаты в вашемKeyStoreи не будет доверять ничего другого, даже если ваша система доверяет им по умолчанию. (Если у вас есть только самоподписанные сертификаты в вашемKeyStoreи попробуйте подключиться к главной странице Google через HTTPS вы получитеSSLHandshakeException).вы можете приобрести
KeyStoreэкземпляр из файла, как показано в docs:KeyStore readKeyStore() { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); // get user password and file input stream char[] password = getPassword(); java.io.FileInputStream fis = null; try { fis = new java.io.FileInputStream("keyStoreName"); ks.load(fis, password); } finally { if (fis != null) { fis.close(); } } return ks; }если вы находитесь на android, вы можете поместить его в
для okhttp3.Охттпклиент версия com.квадрат.okhttp3: okhttp: 3.2.0 вы должны использовать код ниже:
import okhttp3.Call; import okhttp3.Cookie; import okhttp3.CookieJar; import okhttp3.Headers; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; ...... OkHttpClient.Builder clientBuilder = client.newBuilder().readTimeout(LOGIN_TIMEOUT_SEC, TimeUnit.SECONDS); boolean allowUntrusted = true; if ( allowUntrusted) { Log.w(TAG,"**** Allow untrusted SSL connection ****"); final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { X509Certificate[] cArrr = new X509Certificate[0]; return cArrr; } @Override public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { } @Override public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { } }}; SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); clientBuilder.sslSocketFactory(sslContext.getSocketFactory()); HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { Log.d(TAG, "Trust Host :" + hostname); return true; } }; clientBuilder.hostnameVerifier( hostnameVerifier); } final Call call = clientBuilder.build().newCall(request);
два метода из нашего приложения, чтобы получить OkHttpClient 3.0 экземпляр, который распознает ваши самозаверяющие сертификаты из вашего хранилища ключей (использует подготовленный файл сертификата pkcs12 в вашем проекте Android "raw" папка "Resources"):
private static OkHttpClient getSSLClient(Context context) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, CertificateException, IOException { OkHttpClient client; SSLContext sslContext; SSLSocketFactory sslSocketFactory; TrustManager[] trustManagers; TrustManagerFactory trustManagerFactory; X509TrustManager trustManager; trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(readKeyStore(context)); trustManagers = trustManagerFactory.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); } trustManager = (X509TrustManager) trustManagers[0]; sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{trustManager}, null); sslSocketFactory = sslContext.getSocketFactory(); client = new OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustManager) .build(); return client; } /** * Get keys store. Key file should be encrypted with pkcs12 standard. It can be done with standalone encrypting java applications like "keytool". File password is also required. * * @param context Activity or some other context. * @return Keys store. * @throws KeyStoreException * @throws CertificateException * @throws NoSuchAlgorithmException * @throws IOException */ private static KeyStore readKeyStore(Context context) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException { KeyStore keyStore; char[] PASSWORD = "12345678".toCharArray(); ArrayList<InputStream> certificates; int certificateIndex; InputStream certificate; certificates = new ArrayList<>(); certificates.add(context.getResources().openRawResource(R.raw.ssl_pkcs12)); keyStore = KeyStore.getInstance("pkcs12"); for (Certificate certificate : certificates) { try { keyStore.load(certificate, PASSWORD); } finally { if (certificate != null) { certificate.close(); } } } return keyStore; }
против Retrofit 1.9 я смог принять любой сертификат со следующей стратегией: используйте на свой страх и риск! Принятие любого сертификата опасно, и вы должны понимать последствия. Некоторые соответствующие части приходят от
org.apache.http.ssl, поэтому вам может потребоваться некоторый импорт здесь.// ... Client httpClient = getHttpClient(); RestAdapter adapter = new RestAdapter.Builder() .setClient(httpClient) // ... the rest of your builder setup .build(); // ... private Client getHttpClient() { try { // Allow self-signed (and actually any) SSL certificate to be trusted in this context TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() .loadTrustMaterial(null, acceptingTrustStrategy) .build(); sslContext.getSocketFactory(); SSLSocketFactory sf = sslContext.getSocketFactory(); OkHttpClient client = new OkHttpClient(); client.setSslSocketFactory(sf); return new OkClient(client); } catch (Exception e) { throw new RuntimeException("Failed to create new HTTP client", e); } }
следующий фрагмент кода позволяет создать клиент OkHttp, который можно использовать с Retrofit. Ответ Mailmustdie "лучше" в том смысле, что он более безопасен, но приведенный ниже фрагмент кода быстрее реализовать
import com.squareup.okhttp.Headers; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.RequestBody; import com.squareup.okhttp.ResponseBody; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import okio.BufferedSink; import retrofit.client.Header; import retrofit.client.OkClient; import retrofit.client.Request; import retrofit.client.Response; import retrofit.mime.TypedInput; import retrofit.mime.TypedOutput; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; public class TrustingOkClient extends OkClient { static final int CONNECT_TIMEOUT_MILLIS = 15 * 1000; // 15s static final int READ_TIMEOUT_MILLIS = 20 * 1000; // 20s private static OkHttpClient generateDefaultOkHttp() { OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); final TrustManager[] certs = new TrustManager[]{new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { } @Override public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { } }}; SSLContext ctx = null; try { ctx = SSLContext.getInstance("TLS"); ctx.init(null, certs, new SecureRandom()); } catch (final java.security.GeneralSecurityException ex) { } try { final HostnameVerifier hostnameVerifier = new HostnameVerifier() { @Override public boolean verify(final String hostname, final SSLSession session) { return true; } }; client.setHostnameVerifier(hostnameVerifier); client.setSslSocketFactory(ctx.getSocketFactory()); } catch (final Exception e) { } return client; } private final OkHttpClient client; public TrustingOkClient() { this(generateDefaultOkHttp()); } public TrustingOkClient(OkHttpClient client) { if (client == null) throw new NullPointerException("client == null"); this.client = client; } @Override public Response execute(Request request) throws IOException { return parseResponse(client.newCall(createRequest(request)).execute()); } static com.squareup.okhttp.Request createRequest(Request request) { com.squareup.okhttp.Request.Builder builder = new com.squareup.okhttp.Request.Builder() .url(request.getUrl()) .method(request.getMethod(), createRequestBody(request.getBody())); List<Header> headers = request.getHeaders(); for (int i = 0, size = headers.size(); i < size; i++) { Header header = headers.get(i); String value = header.getValue(); if (value == null) value = ""; builder.addHeader(header.getName(), value); } return builder.build(); } static Response parseResponse(com.squareup.okhttp.Response response) { return new Response(response.request().urlString(), response.code(), response.message(), createHeaders(response.headers()), createResponseBody(response.body())); } private static RequestBody createRequestBody(final TypedOutput body) { if (body == null) { return null; } final MediaType mediaType = MediaType.parse(body.mimeType()); return new RequestBody() { @Override public MediaType contentType() { return mediaType; } @Override public void writeTo(BufferedSink sink) throws IOException { body.writeTo(sink.outputStream()); } @Override public long contentLength() { return body.length(); } }; } private static TypedInput createResponseBody(final ResponseBody body) { try { if (body.contentLength() == 0) { return null; } return new TypedInput() { @Override public String mimeType() { MediaType mediaType = body.contentType(); return mediaType == null ? null : mediaType.toString(); } @Override public long length() { try { return body.contentLength(); } catch (Exception exception) { System.out.println(exception.toString()); } throw new Error("createResponseBody has invalid length for its response"); } @Override public InputStream in() throws IOException { return body.byteStream(); } }; } catch (Exception exception) { System.out.println(exception.toString()); } throw new Error("createResponseBody has invalid content length for its response"); } private static List<Header> createHeaders(Headers headers) { int size = headers.size(); List<Header> headerList = new ArrayList<Header>(size); for (int i = 0; i < size; i++) { headerList.add(new Header(headers.name(i), headers.value(i))); } return headerList; } }
Comments