本文介绍了给出错误的Android棒棒糖前置设备"SSL握手已中止:ssl = 0x618d9c18:系统调用期间I/O错误,对等方重置连接"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个奇怪的问题,在该问题中,改装不断使我失望

Iam having this strange issue in which the retrofit keeps throwing me

在kitkat中使用

,而相同的代码在棒棒糖设备中也可以正常工作.我使用类似于以下内容的OkHttpClient客户端

in kitkat, whereas the same code working fine in lollipop devices. Iam using an OkHttpClient client like the following

public OkHttpClient getUnsafeOkHttpClient() {
    try {
        final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] chain,
                    String authType) {
            }
            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] chain,
                    String authType) {
            }
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[0];
            }
        } };

        int cacheSize = 10 * 1024 * 1024; // 10 MB
        Cache cache = new Cache(getCacheDir(), cacheSize);
        final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, trustAllCerts,
                new java.security.SecureRandom());
        final SSLSocketFactory sslSocketFactory = sslContext
                .getSocketFactory();
        OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient = okHttpClient.newBuilder()
                .cache(cache)
                .sslSocketFactory(sslSocketFactory)
                .hostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER).build();
        return okHttpClient;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

}

我是在这样的改造中使用此客户端

Iam using this client in retrofit like this

Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(URL)
            .client(getUnsafeOkHttpClient())
            .addConverterFactory(GsonConverterFactory.create())
            .build();

此处添加getUnsafeOkHttpClient()无效,完全不建议使用getUnsafeOkHttpClient()

EDIT : adding the getUnsafeOkHttpClient() has no effect here and it is not at all recommended to bypass the ssl check by using getUnsafeOkHttpClient()

仅供参考:问题是因为api端点仅支持 TLS 1.2 ,而android设备 16<device<20 默认禁用了该功能.因此,对于 16<device<20 ,创建自定义SSLSocketFactory

FYI : The issue was because the api endpoint supports only TLS 1.2 which was disabled by default on android devices 16<device<20 . So for 16<device<20, create a custom SSLSocketFactory

推荐答案

最终找到了解决此问题的方法,它不是一个完整的解决方案,因为它是来自 okhttp,方形 此处.正如我提到的那样,这是一个简单的hack,必须将我的 SSLSocketFactory 变量重命名为

Finally found a solution to this issue, its not a complete solution as it is a hack mentioned by Jesse Wilson from okhttp, square here. As i mentioned it was a simple hack where i had to rename my SSLSocketFactory variable to

private SSLSocketFactory delegate;

请注意,如果您输入除委托之外的任何其他名称,它将引发错误. Iam在下面发布我的完整解决方案

notice that it would throw error if you give any name other than delegate. Iam posting my complete solution below

这是我的 TLSSocketFactory

public class TLSSocketFactory extends SSLSocketFactory {

private SSLSocketFactory delegate;
private TrustManager[] trustManagers;

public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
    generateTrustManagers();
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, trustManagers, null);
    delegate = context.getSocketFactory();
}

private void generateTrustManagers() throws KeyStoreException, NoSuchAlgorithmException {
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init((KeyStore) null);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

    if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
                + Arrays.toString(trustManagers));
    }

    this.trustManagers = trustManagers;
}


@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
}

@Override
public Socket createSocket() throws IOException {
    return enableTLSOnSocket(delegate.createSocket());
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    return enableTLSOnSocket(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
    return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}

private Socket enableTLSOnSocket(Socket socket) {
    if(socket != null && (socket instanceof SSLSocket)) {
        ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
    }
    return socket;
}

@Nullable
public X509TrustManager getTrustManager() {
    return  (X509TrustManager) trustManagers[0];
}

}

这就是我在okhttp和改造中使用它的方式

and this is how i used it with okhttp and retrofit

 OkHttpClient client=new OkHttpClient();
    try {
        TLSSocketFactory tlsSocketFactory=new TLSSocketFactory();
        if (tlsSocketFactory.getTrustManager()!=null) {
            client = new OkHttpClient.Builder()
                    .sslSocketFactory(tlsSocketFactory, tlsSocketFactory.getTrustManager())
                    .build();
        }
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    }

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

EDIT :方法public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory)现在已被弃用,我们应该使用public Builder sslSocketFactory( SSLSocketFactory sslSocketFactory, X509TrustManager trustManager),因为我在答案中已进行了更新.这是因为X509TrustManager是OkHttp需要建立一个干净的证书链的字段,在不赞成使用的方法中该字段没有停止.

EDIT : The method public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) is now deprecated and we should use public Builder sslSocketFactory( SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) as i have updated in the answer. This is because X509TrustManager is a field that OkHttp needs to build a clean certificate chain, which was not paased in the deprecated method.

您还可以查看以获取更多信息

这篇关于给出错误的Android棒棒糖前置设备"SSL握手已中止:ssl = 0x618d9c18:系统调用期间I/O错误,对等方重置连接"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-09 12:10