问题描述
我遇到了一个奇怪的问题,改装一直让我失望
Iam having this strange issue in which the retrofit keeps throwing me
"SSL 握手中止:ssl=0x618d9c18:系统调用期间出现 I/O 错误,对等方重置连接"
在 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();
EDIT : 添加 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
.所以对于16
,创建一个自定义的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
推荐答案
终于找到了这个问题的解决方案,它不是一个完整的解决方案,因为它是 Jesse Wilson 来自 okhttp, square 这里.正如我提到的,这是一个简单的 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;
请注意,如果您提供除委托之外的任何名称,它将引发错误.我在下面发布我的完整解决方案
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构建干净证书链需要的一个字段,在弃用的方法中没有paased.
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 错误,连接由对等方重置"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!