Ich arbeite für einen Kunden, der einen Server mit selbstsigniertem SSL-Zertifikat hat.
Ich verwende Retrofit + CustomClient mit dem umhüllten OkHttp-Client:
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(Config.BASE_URL + Config.API_VERSION)
.setClient(new CustomClient(new OkClient(), context))
.build();
Unterstützt OkHttp standardmäßig das Aufrufen eines selbstsignierten SSL-Zertifizierungsservers?
Apropos. Welcher Client verwendet standardmäßig Retrofit? Ich dachte, es wäre OkHttp, aber als ich ein bisschen mehr recherchierte, wurde mir klar, dass ich OkHttp-Abhängigkeiten importieren musste
defaultClient()
in Platform.java .Antworten:
Ja tut es.
Mit Retrofit können Sie Ihren benutzerdefinierten HTTP-Client einstellen, der Ihren Anforderungen entsprechend konfiguriert ist.
Für selbstsignierte SSL-Zertifikate wird hier diskutiert . Der Link enthält Codebeispiele zum Hinzufügen selbstsignierter SLL zu Android
DefaultHttpClient
und zum Laden dieses Clients in Retrofit.Wenn Sie
OkHttpClient
selbstsigniertes SSL akzeptieren müssen, müssen Sie die benutzerdefiniertejavax.net.ssl.SSLSocketFactory
Instanz über diesetSslSocketFactory(SSLSocketFactory sslSocketFactory)
Methode übergeben.Die einfachste Methode, um eine Sockelfabrik zu erhalten, besteht darin, eine
javax.net.ssl.SSLContext
wie hier beschrieben zu beziehen .Hier ist ein Beispiel für die Konfiguration von 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());
Aktualisierter Code für okhttp3 (mit Builder):
OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory()) .build();
Das
client
hier ist jetzt so konfiguriert, dass Zertifikate von Ihrem verwendet werdenKeyStore
. Es vertraut jedoch nur den Zertifikaten in IhremKeyStore
und nichts anderem, selbst wenn Ihr System ihnen standardmäßig vertraut. (Wenn Sie nur selbstsignierte Zertifikate in Ihrem habenKeyStore
und versuchen, über HTTPS eine Verbindung zur Google-Hauptseite herzustellen, erhalten SieSSLHandshakeException
).Sie können die
KeyStore
Instanz aus einer Datei abrufen, wie in den Dokumenten gezeigt :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; }
Wenn Sie auf Android sind, können Sie es in einen
res/raw
Ordner legen und von einerContext
Instanz mit abrufenEs gibt verschiedene Diskussionen zum Erstellen Ihres Keystores. Zum Beispiel hier
quelle
Für okhttp3.OkHttpClient Version com.squareup.okhttp3: okhttp: 3.2.0 müssen Sie den folgenden Code verwenden:
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);
quelle
Wenn Sie die Zertifizierungsstelle auf dem Gerät vorinstallieren, können Sie außerdem regelmäßig https-Aufrufe mit OKHttp und ohne spezielle SSL-Rahmen ausführen. Der Schlüssel besteht darin, die Netzwerksicherheitskonfigurationen zu Ihrem Manifest hinzuzufügen.
Der Schlüssel für mich, dies zu wissen, war, dass ich die folgende Ausnahme bekam.
" Vertrauensanker für Zertifizierungspfad nicht gefunden. "
Hier ist ein guter Artikel von Google über die Konfiguration. https://developer.android.com/training/articles/security-config
Hier ist ein Beispiel für meine network_security_config.xml
<?xml version="1.0" encoding="UTF-8" ?> <network-security-config> <base-config cleartextTrafficPermitted="false"> <trust-anchors> <certificates src="user"/> <certificates src="system"/> </trust-anchors> </base-config> </network-security-config>
quelle
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Zwei Methoden aus unserer App, um eine OkHttpClient 3.0- Instanz abzurufen , die Ihre selbstsignierten Zertifikate aus Ihrem Keystore erkennt (verwendet die vorbereitete pkcs12-Zertifikatdatei in Ihrem "rohen" Ressourcenordner Ihres Android-Projekts ):
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; }
quelle
certificates.add(context.getResources().openRawResource(R.raw.ssl_pkcs12));
Diese Zeile zeigen Fehler können Sie bitte den Grund angeben, warum? Gibt es einen Grund, dies aus der Nicht-Aktivitätsklasse zu klassifizierenIch hatte das gleiche Problem und habe es mit dem okhttp- Client wie folgt behoben :
1.) Fügen Sie die
certificate
Datei hinzusrc/main/res/raw/
, die diesen Inhalt enthält:2.) Instanziieren Sie den okHttpClient:
OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(getSslContext(context).getSocketFactory()) .build();
3.) Hier ist die verwendete
getSslContext(Context context)
Methode:SSLContext getSslContext(Context context) throws Exception { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); // "BKS" ks.load(null, null); InputStream is = context.getResources().openRawResource(R.raw.certificate); String certificate = Converter.convertStreamToString(is); // generate input stream for certificate factory InputStream stream = IOUtils.toInputStream(certificate); // CertificateFactory CertificateFactory cf = CertificateFactory.getInstance("X.509"); // certificate Certificate ca; try { ca = cf.generateCertificate(stream); } finally { is.close(); } ks.setCertificateEntry("my-ca", ca); // TrustManagerFactory String algorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); // Create a TrustManager that trusts the CAs in our KeyStore tmf.init(ks); // Create a SSLContext with the certificate that uses tmf (TrustManager) sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), new SecureRandom()); return sslContext; }
quelle
Gegen Retrofit 1.9 konnte ich jedes Zertifikat mit folgender Strategie akzeptieren: Verwendung auf eigenes Risiko! Das Akzeptieren eines Zertifikats ist gefährlich und Sie sollten die Konsequenzen verstehen. Einige relevante Teile stammen von
org.apache.http.ssl
, daher müssen Sie hier möglicherweise importieren.// ... 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); } }
quelle
Ich weiß, dass dieser Beitrag ziemlich alt ist, aber ich möchte die Lösung, die für mich funktioniert hat, mit dem neuesten Update von OkHttp teilen, der
3.12.1
Version in der Zeit , in der ich schreibe.Zunächst müssen Sie das KeyStore-Objekt erhalten, das dann dem TrustManager hinzugefügt wird:
/** * @param context The Android context to be used for retrieving the keystore from raw resource * @return the KeyStore read or null on error */ private static KeyStore readKeyStore(Context context) { char[] password = "keystore_password".toCharArray(); // for non-android usage: // try(FileInputStream is = new FileInputStream(keystoreName)) { try(InputStream is = context.getResources().openRawResource(R.raw.keystore)) { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(is, password); return ks; } catch (CertificateException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } return null; }
Jetzt können Sie das
OkHttpClient
mit dem selbstsignierten Zertifikat erstellte Zertifikat in Ihrem Schlüsselspeicher erhalten:/** * @param context The Android context used to obtain the KeyStore * @return the builded OkHttpClient or null on error */ public static OkHttpClient getOkHttpClient(Context context) { try { TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(readKeyStore(context)); X509TrustManager trustManager = (X509TrustManager) trustManagerFactory.getTrustManagers()[0]; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{trustManager}, null); return new OkHttpClient.Builder() .hostnameVerifier((hostname, session) -> { HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); /* Never return true without verifying the hostname, otherwise you will be vulnerable to man in the middle attacks. */ return hv.verify("your_hostname_here", session); }) .sslSocketFactory(sslContext.getSocketFactory(), trustManager) .build(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyStoreException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; }
Denken Sie daran, dass es sehr entmutigt ist , immer wahr zurückzukehren
hostnameVerifier
, um das Risiko eines Menschen bei mittleren Angriffen zu vermeiden.quelle
Mit dem folgenden Code können Sie einen OkHttp-Client erstellen, der mit Retrofit verwendet werden kann. Mailmustdies Antwort ist "besser" in dem Sinne, dass sie sicherer ist, aber das folgende Codefragment ist schneller zu implementieren
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; } }
quelle