OkHttp3 hostnameVerifier原因:javax.net.ssl.SSLException:读取错误:ssl = 0xc8cf1fc8:系统调用期间I / O错误,对等方重置连接

时间:2019-11-11 14:49:16

标签: android ssl android-volley okhttp tls1.2

我已经在SO上发表过类似的帖子,指出了类似的问题。 就我而言,我尝试在API级28的Nexus 6p模拟器上运行。

此实现适用于Volley HurlStack,但不适用于OkHttp3 HttpStack。因此,我确定服务器端没有问题,但OkHttp方面没有问题。不知道我在这里想念什么。任何线索将不胜感激。预先感谢!

PS 我正准备远离Volley。这只是使用okHttp进行测试,以便更轻松地进行迁移。

排球工作实施如下:

 import android.annotation.SuppressLint;
 import android.util.Base64;

 import com.abc.test.core.network.Tls12SocketFactory;
 import com.android.volley.toolbox.HurlStack;

 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.security.KeyManagementException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;

 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;

 import timber.log.Timber;

public class VolleyHurlStack extends HurlStack {

private final Boolean enablePinning;

VolleyHurlStack(Boolean enablePinning) {
    this.enablePinning = enablePinning;
}

@Override
protected HttpURLConnection createConnection(URL url) throws IOException {
    HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url);

    try {
        httpsURLConnection.setSSLSocketFactory(getSSLSocketFactory());
        httpsURLConnection.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {

                try {
                    return validatePinning(session.getPeerCertificates());
                } catch (SSLPeerUnverifiedException e) {
                    Timber.e(e);
                }

                return false;
            }
        });

    } catch (Exception e) {
        Timber.e(e);
    }
    return httpsURLConnection;
}

private SSLSocketFactory getSSLSocketFactory() {

    SSLContext sc;
    SSLSocketFactory sslSocketFactory = null;
    try {
        sc = SSLContext.getInstance("TLSv1.1");
        if (sc != null) {
            sc.init(null, getAllTrustManagers(), new SecureRandom());
            sslSocketFactory = new Tls12SocketFactory(sc.getSocketFactory());
        } else {
            Timber.e("SSLContext is null");
        }
    } catch (NoSuchAlgorithmException | KeyManagementException e) {
        Timber.e(e);
    }

    return sslSocketFactory;
}


private TrustManager[] getAllTrustManagers() {
    return new TrustManager[]{getX509TrustManager()};
}


private X509TrustManager getX509TrustManager() {
    return new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }

        @SuppressLint("TrustAllX509TrustManager")
        @Override
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }

        @SuppressLint("TrustAllX509TrustManager")
        @Override
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    };
}

public boolean validatePinning(Certificate[] certificate) {

    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");

        for (Certificate cert : certificate) {
            byte[] publicKey = cert.getPublicKey().getEncoded();
            md.update(publicKey, 0, publicKey.length);
            String pin = Base64.encodeToString(md.digest(), Base64.NO_WRAP);

            for (String validPin : validPins) {
                if (validPin.contains(pin)) {
                    Timber.d("validatePinning successful");
                    return true;
                }
            }
        }
    } catch (NoSuchAlgorithmException e) {
        Timber.e(e);
    }

    return false;
 }

}

非正常的OkHttp实现如下:

 import android.annotation.SuppressLint;
 import android.util.Base64;

 import com.abc.test.core.network.Tls12SocketFactory;
 import com.android.volley.AuthFailureError;
 import com.android.volley.Header;
 import com.android.volley.Request;
 import com.android.volley.toolbox.BaseHttpStack;
 import com.android.volley.toolbox.HttpResponse;

 import java.io.IOException;
 import java.io.InputStream;
 import java.security.KeyManagementException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;

 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509TrustManager;

 import okhttp3.Call;
 import okhttp3.ConnectionSpec;
 import okhttp3.Headers;
 import okhttp3.MediaType;
 import okhttp3.OkHttpClient;
 import okhttp3.RequestBody;
 import okhttp3.Response;
 import okhttp3.ResponseBody;
 import timber.log.Timber;

 public class OkHttp3Stack extends BaseHttpStack {


public OkHttp3Stack() {

}

private static void setConnectionParametersForRequest(okhttp3.Request.Builder builder, Request<?> request)
        throws AuthFailureError {
    switch (request.getMethod()) {
        case Request.Method.DEPRECATED_GET_OR_POST:
            // Ensure backwards compatibility.  Volley assumes a request with a null body is a GET.
            byte[] postBody = request.getBody();
            if (postBody != null) {
                builder.post(RequestBody.create(MediaType.parse(request.getBodyContentType()), postBody));
            }
            break;
        case Request.Method.GET:
            builder.get();
            break;
        case Request.Method.DELETE:
            builder.delete(createRequestBody(request));
            break;
        case Request.Method.POST:
            builder.post(createRequestBody(request));
            break;
        case Request.Method.PUT:
            builder.put(createRequestBody(request));
            break;
        case Request.Method.HEAD:
            builder.head();
            break;
        case Request.Method.OPTIONS:
            builder.method("OPTIONS", null);
            break;
        case Request.Method.TRACE:
            builder.method("TRACE", null);
            break;
        case Request.Method.PATCH:
            builder.patch(createRequestBody(request));
            break;
        default:
            throw new IllegalStateException("Unknown method type.");
    }
}

private static RequestBody createRequestBody(Request r) throws AuthFailureError {
    final byte[] body = r.getBody();
    if (body == null) {
        return null;
    }
    return RequestBody.create(MediaType.parse(r.getBodyContentType()), body);
}

@Override
public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
    OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
    int timeoutMs = request.getTimeoutMs();

    clientBuilder.connectTimeout(timeoutMs, TimeUnit.MILLISECONDS);
    clientBuilder.readTimeout(timeoutMs, TimeUnit.MILLISECONDS);
    clientBuilder.writeTimeout(timeoutMs, TimeUnit.MILLISECONDS);

    okhttp3.Request.Builder okHttpRequestBuilder = new okhttp3.Request.Builder();
    okHttpRequestBuilder.url(request.getUrl());

    Map<String, String> headers = request.getHeaders();
    for (final String name : headers.keySet()) {
        okHttpRequestBuilder.addHeader(name, headers.get(name));
    }
    for (final String name : additionalHeaders.keySet()) {
        okHttpRequestBuilder.addHeader(name, additionalHeaders.get(name));
    }

    setConnectionParametersForRequest(okHttpRequestBuilder, request);


    clientBuilder.sslSocketFactory(getSSLSocketFactory(), getX509TrustManager());
    clientBuilder.hostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {

            try {
                return validatePinning(session.getPeerCertificates());
            } catch (SSLPeerUnverifiedException e) {
                Timber.e(e);
            }

            return false;
        }
    });

    ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
            .allEnabledTlsVersions()
            .allEnabledCipherSuites()
            .build();

    clientBuilder.connectionSpecs(Collections.singletonList(spec));

    clientBuilder.retryOnConnectionFailure(true);


    OkHttpClient client = clientBuilder.build();
    okhttp3.Request okHttpRequest = okHttpRequestBuilder.build();
    Call okHttpCall = client.newCall(okHttpRequest);
    Response okHttpResponse = okHttpCall.execute();


    int code = okHttpResponse.code();
    ResponseBody body = okHttpResponse.body();
    InputStream content = body == null ? null : body.byteStream();
    int contentLength = body == null ? 0 : (int) body.contentLength();
    List<Header> responseHeaders = mapHeaders(okHttpResponse.headers());
    return new HttpResponse(code, responseHeaders, contentLength, content);
}

private List<Header> mapHeaders(Headers responseHeaders) {
    List<Header> headers = new ArrayList<>();
    for (int i = 0, len = responseHeaders.size(); i < len; i++) {
        final String name = responseHeaders.name(i), value = responseHeaders.value(i);
        if (name != null) {
            headers.add(new Header(name, value));
        }
    }
    return headers;
}


private SSLSocketFactory getSSLSocketFactory() {

    SSLContext sc;
    SSLSocketFactory sslSocketFactory = null;
    try {
        sc = SSLContext.getInstance("TLSv1.1");
        if (sc != null) {
            sc.init(null, getAllTrustManagers(), new SecureRandom());
            sslSocketFactory = new Tls12SocketFactory(sc.getSocketFactory());
        } else {
            Timber.e("SSLContext is null");
        }
    } catch (NoSuchAlgorithmException | KeyManagementException e) {
        Timber.e(e);
    }

    return sslSocketFactory;
}


private TrustManager[] getAllTrustManagers() {
    return new TrustManager[]{getX509TrustManager()};
}


private X509TrustManager getX509TrustManager() {
    return new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }

        @SuppressLint("TrustAllX509TrustManager")
        @Override
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }

        @SuppressLint("TrustAllX509TrustManager")
        @Override
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    };
}

private boolean validatePinning(Certificate[] certificate) {

    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");

        for (Certificate cert : certificate) {
            byte[] publicKey = cert.getPublicKey().getEncoded();
            md.update(publicKey, 0, publicKey.length);
            String pin = Base64.encodeToString(md.digest(), Base64.NO_WRAP);

            for (String validPin : validPins) {
                if (validPin.contains(pin)) {
                    Timber.d("validatePinning successful");
                    return true;
                }
            }
        }
    } catch (NoSuchAlgorithmException e) {
        Timber.e(e);
    }

    return false;
  }
 }

1 个答案:

答案 0 :(得分:0)

显然,传递默认用户代理值存在问题,该值在后端被阻止

与okhttp一起传递的User-Agent值为:okhttp 开始传递自定义用户代理值,问题已解决。

它没有作为okhttpInterceptor的一部分记录下来,因此跟踪两个请求中的差异花费了很多时间。不要期望用户代理会成为罪魁祸首。