OkHttp支持接受自签名SSL证书吗?

我正在为拥有带有自签名SSL证书的服务器的客户工作。

我正在使用包装的OkHttp客户端使用Retrofit + CustomClient:

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

OkHttp默认支持调用自签名SSL证书服务器吗?

顺便一提。 哪个客户端默认使用Retrofit? 我认为这是OkHttp,但是当我研究了一点,我意识到我需要导入OkHttp依赖项

是的,它确实。

Retrofit允许您设置您的自定义HTTP客户端,即根据您的需要进行configuration。

至于自签名的SSL证书, 这里有一个讨论。 该链接包含代码示例,将自签名SLL添加到Android的DefaultHttpClient并将此客户端加载到Retrofit。

如果您需要OkHttpClient接受自签名SSL,则需要通过setSslSocketFactory(SSLSocketFactory sslSocketFactory)方法将自定义javax.net.ssl.SSLSocketFactory实例传递给它。

获取套接字工厂的最简单方法是从javax.net.ssl.SSLContext获取一个,如此处所述。

这里是configurationOkHttpClient的示例:

 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的更新代码(使用构build器):

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

这里的client现在被configuration为使用您的KeyStore证书。 但是,只会信任您的KeyStore的证书,即使您的系统默认信任它们,也不会信任任何其他信息。 (如果您的KeyStore只有自签名证书,并尝试通过HTTPS连接到Google主页,则会得到SSLHandshakeException )。

您可以从文件中获取KeyStore实例,如文档中所示 :

 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上,你可以把它放在res/raw文件夹中,并从一个Context实例中获取

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

有几个关于如何创build密钥库的讨论。 例如这里

对于okhttp3.OkHttpClient版本com.squareup.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实例,从您的密钥库中识别您的自签名证书(使用您的Android项目“原始”资源文件夹中准备好的pkcs12证书文件):

 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 ( certificateIndex = 0; certificateIndex < certificates.size(); certificateIndex++) { certificate = certificates.get( certificateIndex); try { keyStore.load( certificate, PASSWORD); } finally { if (certificate != null) { certificate.close(); } } } keyStore.size(); return keyStore; } 

下面这段代码可以让你创build一个可以与Retrofit一起使用的OkHttp客户端。 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; } }