O OkHttp suporta a aceitação de certificados SSL autoassinados?

Estou trabalhando para um cliente que possui um servidor com certificado SSL autoassinado.

Estou usando o Retrofit + CustomClient usando o cliente OkHttp envolto:

RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(Config.BASE_URL + Config.API_VERSION) .setClient(new CustomClient(new OkClient(), context)) .build(); 

O OkHttp suporta chamada de servidor cert SSL auto-assinado por padrão?

A propósito. Qual cliente está usando Retrofit por padrão? Eu pensei que era OkHttp mas quando eu pesquisei um pouco mais percebi que precisava importar as dependencies do OkHttp

Sim.

Retrofit permite que você defina seu cliente HTTP personalizado, configurado de acordo com suas necessidades.

Quanto aos certificados SSL auto-assinados, há uma discussão aqui . O link contém exemplos de código para adicionar SLL auto-assinado ao DefaultHttpClient do Android e para carregar esse cliente para o Retrofit.

Se você precisar que o OkHttpClient aceite o SSL autoassinado, será necessário transmiti-lo à instância setSslSocketFactory(SSLSocketFactory sslSocketFactory) método setSslSocketFactory(SSLSocketFactory sslSocketFactory) .

O método mais fácil de obter uma fábrica de sockets é obter um de javax.net.ssl.SSLContext como discutido aqui .

Aqui está um exemplo para configurar o 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()); 

Código atualizado para okhttp3 (usando o construtor):

  OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory()) .build(); 

o client aqui agora está configurado para usar certificados de seu KeyStore . No entanto, ele confiará somente nos certificados do KeyStore e não confiará em mais nada, mesmo que o sistema confie neles por padrão. (Se você tiver apenas certs auto-assinados em seu KeyStore e tentar se conectar à página principal do Google via HTTPS, você obterá SSLHandshakeException ).

Você pode obter a instância do KeyStore do arquivo conforme visto nos documentos :

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

Se você está no android você pode colocá-lo na pasta res/raw e obtê-lo de uma instância de Context usando

 fis = context.getResources().openRawResource(R.raw.your_keystore_filename); 

Existem várias discussões sobre como criar seu keystore. Por exemplo aqui

Para okhttp3.OkHttpClient Version com.squareup.okhttp3: okhttp: 3.2.0 você tem que usar o código abaixo:

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

Dois methods de nosso aplicativo para obter a instância OkHttpClient 3.0 que reconhece seus certificados autoassinados de seu keystore (usa o arquivo de certificado pkcs12 preparado na pasta de resources “brutos” do projeto Android):

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

Contra o Retrofit 1.9 Pude aceitar qualquer certificado com a seguinte estratégia: use a seu próprio risco! Aceitar qualquer certificado é perigoso e você deve entender as conseqüências. Algumas partes relevantes vêm do org.apache.http.ssl , então você pode precisar de algumas importações aqui.

 // ... 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); } } 

O seguinte trecho de código permite que você crie um cliente OkHttp que possa ser usado com Retrofit. A resposta de Mailmustdie é “melhor” no sentido de que é mais seguro, mas o snippet de código abaixo é mais rápido de implementar

 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
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
createHeaders(Headers headers) { int size = headers.size(); List
headerList = new ArrayList
(size); for (int i = 0; i < size; i++) { headerList.add(new Header(headers.name(i), headers.value(i))); } return headerList; } }