Question OkHttp prend-il en charge l'acceptation de certificats SSL auto-signés?


Je travaille pour un client qui possède un serveur avec certificat SSL auto-signé.

J'utilise Retrofit + CustomClient en utilisant le client OkHttp encapsulé:

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

OkHttp prend-il en charge l'appel par défaut du serveur de certificats SSL auto-signé?

Au fait. Quel client utilise Retrofit par défaut? Je pensais que c'était OkHttp mais quand j'ai recherché un peu plus, j'ai réalisé que je devais importer des dépendances OkHttp


53
2018-04-16 07:49


origine


Réponses:


Oui.

Retrofit vous permet de définir votre client HTTP personnalisé, configuré selon vos besoins.

Comme pour les certificats SSL auto-signés, il y a une discussion ici. Le lien contient des exemples de code pour ajouter SLL auto-signé à Android DefaultHttpClient et pour charger ce client dans Retrofit.

Si tu as besoin OkHttpClient pour accepter le protocole SSL auto-signé, vous devez le transmettre javax.net.ssl.SSLSocketFactory par exemple via setSslSocketFactory(SSLSocketFactory sslSocketFactory) méthode.

La méthode la plus simple pour obtenir une fabrique de sockets consiste à javax.net.ssl.SSLContext Tel que discuté ici.

Voici un exemple pour configurer 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());

Code mis à jour pour okhttp3 (en utilisant le constructeur):

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

la client ici est maintenant configuré pour utiliser des certificats de votre KeyStore. Cependant, il ne fera confiance qu'aux certificats de votre KeyStore et ne fera confiance à rien d'autre, même si votre système leur fait confiance par défaut. (Si vous n'avez que des certificats auto-signés dans votre KeyStore  et essayez de vous connecter à la page principale de Google via HTTPS, vous obtiendrez SSLHandshakeException).

Vous pouvez obtenir KeyStore instance du fichier comme vu dans 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;
}

Si vous êtes sur Android, vous pouvez le mettre dans res/raw dossier et l'obtenir d'un Context instance utilisant

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

Il existe plusieurs discussions sur la création de votre fichier de clés. Par exemple ici


70
2018-06-25 06:36



Pour okhttp3.OkHttpClient Version com.squareup.okhttp3: okhttp: 3.2.0 vous devez utiliser le code ci-dessous:

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

13
2018-05-24 07:25



Deux méthodes de notre application pour obtenir OkHttpClient 3.0 instance qui reconnaît vos certificats auto-signés à partir de votre fichier de clés (utilise le fichier de certificat pkcs12 préparé dans votre projet Android) "brut" dossier ressources):

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

3
2018-05-03 06:54



Contre la rénovation 1.9 J'ai pu accepter n'importe quel certificat avec la stratégie suivante: utiliser à vos risques et périls! Accepter un certificat est dangereux et vous devez en comprendre les conséquences. Certaines parties pertinentes proviennent de org.apache.http.ssl, vous pouvez donc exiger des importations ici.

// ...

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

0
2018-06-26 17:10



Le morceau de code suivant vous permet de créer un client OkHttp pouvant être utilisé avec Retrofit. La réponse de Mailmustdie est "meilleure" dans le sens où elle est plus sécurisée, mais l'extrait de code ci-dessous est plus rapide à mettre en œuvre

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

}

-3
2018-02-01 04:31