【问题标题】:Android pre-lollipop devices giving error "SSL handshake aborted: ssl=0x618d9c18: I/O error during system call, Connection reset by peer"Android pre-lollipop 设备给出错误“SSL 握手中止:ssl=0x618d9c18:系统调用期间 I/O 错误,对等方重置连接”
【发布时间】:2018-02-09 13:51:52
【问题描述】:

我遇到了一个奇怪的问题,改造一直在抛出我

“SSL 握手中止:ssl=0x618d9c18: 系统调用期间的 I/O 错误, 对等方重置连接"

在 kitkat 中,而相同的代码在棒棒糖设备中运行良好。我正在使用 OkHttpClient 客户端,如下所示

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);
    }

}

我正在像这样使用这个客户端进行改造

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

编辑:在此处添加 getUnsafeOkHttpClient() 无效,完全不建议使用 getUnsafeOkHttpClient() 绕过 ssl 检查

仅供参考:问题是因为 api 端点仅支持 TLS 1.2,而 Android 设备默认禁用 16<device<20。所以对于16<device<20,创建一个自定义的SSLSocketFactory

【问题讨论】:

  • 必须使用 TLSv1.2 吗?如果你使用默认值呢?
  • @algrid 如果我更改 ssl 实例没有效果,所以我从改造中删除了整个不安全的客户端,但它仍然只适用于棒棒糖而不适用于 kitkat
  • 你的服务器端有什么?也许你可以从你的服务器获取一些日志?
  • 事实上它是来自 api.data.gov.in 的免费 api,我无权访问服务器日志
  • @algrid 可能的服务器端问题?

标签: android retrofit2 android-4.4-kitkat tls1.2 sslhandshakeexception


【解决方案1】:

终于找到了这个问题的解决方案,它不是一个完整的解决方案,因为它是 Jesse Wilson 提到的一个 hack,来自 okhttp, square here。正如我所提到的,这是一个简单的 hack,我必须将我的 SSLSocketFactory 变量重命名为

private SSLSocketFactory delegate;

请注意,如果您提供除委托以外的任何名称,它将引发错误。我在下面发布我的完整解决方案

这是我的 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 和改造中使用它的方式

 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。

您也可以查看this for more info

【讨论】:

    【解决方案2】:

    我修改了@Navneet Krishna 答案,因为方法 OkHttpClient.Builder。 builder.sslSocketFactory(tlsSocketFactory) 现已弃用。

    public class TLSSocketFactory extends SSLSocketFactory {
    
    private final SSLSocketFactory delegate;
    private TrustManager[] trustManagers;
    
    public TLSSocketFactory() throws KeyStoreException, KeyManagementException, NoSuchAlgorithmException {
        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 instanceof SSLSocket) {
            ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
        }
        return socket;
    }
    
    @Nullable
    public X509TrustManager getTrustManager() {
        return  (X509TrustManager) trustManagers[0];
    }
    }
    

    你需要这样分配它:

    TLSSocketFactory tlsTocketFactory = new TLSSocketFactory();
    client = new OkHttpClient.Builder()
                .sslSocketFactory(tlsSocketFactory, tlsSocketFactory.getTrustManager());
                .build();
    

    【讨论】:

    • 我必须在 TLSSocketFactory 的构造函数中删除 @Inject 才能使其正常工作。也许是错误的导入(对我来说只有一个选项是可能的:导入 javax.inject.Inject,这对我来说没有意义)。附言。在你的答案中包含导入真的很有帮助!
    • 对不起,这是 Dagger DI 中使用的注释。我已经编辑了我的答案。感谢您注意到这一点。
    【解决方案3】:

    除了 Navneet Krishna 之外,我还必须在我的 App 课程中做下一个:

    ProviderInstaller.installIfNeededAsync
    

    根据https://developer.android.com/training/articles/security-gms-provider,这是因为我需要更新安全提供程序以防止 SSL 漏洞。

    我的应用类:

    public class AppClass extends MultiDexApplication {
    
    private static final String TAG = AppClass.class.getName();
    
    private static Context context;
    private static AuthAPI authAPI;
    private static RestAPI buyersAPI;
    
    @Override
    public void onCreate() {
        super.onCreate();
        /* enable SSL compatibility in pre-lollipop devices */
        upgradeSecurityProvider();
    
        createAuthAPI();
        createRestAPI();
    }
    
    private void upgradeSecurityProvider() {
        try{
            ProviderInstaller.installIfNeededAsync(this, new ProviderInstaller.ProviderInstallListener() {
                @Override
                public void onProviderInstalled() {
                    Log.e(TAG, "New security provider installed.");
                }
    
                @Override
                public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
                    GooglePlayServicesUtil.showErrorNotification(errorCode, BuyersApp.this);
                    Log.e(TAG, "New security provider install failed.");
                }
            });
        }catch (Exception ex){
            Log.e(TAG, "Unknown issue trying to install a new security provider", ex);
        }
    
    }
    
    private void createAuthAPI() {
        OkHttpClient.Builder authAPIHttpClientBuilder = new OkHttpClient.Builder();
    
        Retrofit retrofit = new Retrofit.Builder()
                .client(enableTls12OnPreLollipop(authAPIHttpClientBuilder).build())
                .baseUrl(DomainLoader.getInstance(context).getAuthDomain())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        authAPI = retrofit.create(AuthAPI.class);
    }
    
    private static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
        if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 22) {
            try {
                SSLContext sc = SSLContext.getInstance("TLSv1.2");
                sc.init(null, null, null);
    
                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));
                }
                X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
    
                client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()), trustManager);
    
                ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                        .tlsVersions(TlsVersion.TLS_1_2)
                        .build();
    
                List<ConnectionSpec> specs = new ArrayList<>();
                specs.add(cs);
                specs.add(ConnectionSpec.COMPATIBLE_TLS);
                specs.add(ConnectionSpec.CLEARTEXT);
    
                client.connectionSpecs(specs);
            } catch (Exception exc) {
                Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
            }
        }
    
        return client;
    }
    
    private void createRestAPI() {
        OkHttpClient.Builder restAPIHttpClientBuilder = new OkHttpClient.Builder();
        buyersAPIHttpClientBuilder.readTimeout(60, TimeUnit.SECONDS);
        buyersAPIHttpClientBuilder.connectTimeout(60, TimeUnit.SECONDS);
        buyersAPIHttpClientBuilder.writeTimeout(600, TimeUnit.SECONDS);
        buyersAPIHttpClientBuilder.addInterceptor(new NetworkErrorInterceptor());
        buyersAPIHttpClientBuilder.addInterceptor(new TokenVerificationInterceptor());
    
        Retrofit retrofit = new Retrofit.Builder()
                .client(enableTls12OnPreLollipop(restAPIHttpClientBuilder).build())
                .baseUrl(DomainLoader.getInstance(context).getDomain())
                .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().setLenient().create()))
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();
    
        buyersAPI = retrofit.create(RestAPI.class);
    }
    }
    

    还有我的 Tls12SocketFactory 类:

    public class Tls12SocketFactory extends SSLSocketFactory {
    private static final String[] TLS_V12_ONLY = {"TLSv1.2"};
    
    final SSLSocketFactory delegate;
    
    public Tls12SocketFactory(SSLSocketFactory base) {
        this.delegate = base;
    }
    
    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }
    
    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }
    
    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return patch(delegate.createSocket(s, host, port, autoClose));
    }
    
    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return patch(delegate.createSocket(host, port));
    }
    
    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return patch(delegate.createSocket(host, port, localHost, localPort));
    }
    
    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return patch(delegate.createSocket(host, port));
    }
    
    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return patch(delegate.createSocket(address, port, localAddress, localPort));
    }
    
    private Socket patch(Socket s) {
        if (s instanceof SSLSocket) {
            ((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY);
        }
        return s;
    }
    }
    

    它在所有带有 KitKat 及更高版本的设备中都像魅力一样工作。

    【讨论】:

      【解决方案4】:

      我在这里获得了 api.data.gov.in 的 SSL/TLS 信息 - https://www.ssllabs.com/ssltest/analyze.html?d=api.data.gov.in

      看起来它只支持 TLSv1.2。旧的 Android 版本确实与最新的 TLS 版本存在问题。在 ssllabs 页面的“握手模拟”部分,您甚至可以看到您的问题。

      请参阅How to enable TLS 1.2 support in an Android application (running on Android 4.1 JB) 了解可用的解决方案。

      【讨论】:

      • 它对我不起作用,它现在与 Caused by: java.lang.IllegalStateException: Unable to extract the trust manager on okhttp3.internal.platform.AndroidPlatform@422663d0, sslSocketFactory is class navneet.com.devinered.service.TLSSocketFactory 崩溃
      【解决方案5】:

      我认为我的解决方案可能会对某人有所帮助。

      在我的项目中,我需要在较旧的 Android (4.4) 上通过 SSL 执行 JSON 请求,并且一直遇到线程顶部提到的问题。

      要解决这个问题,我所要做的就是像上面一样添加类 Tls12SocketFactory。

      但是,我在我的项目类中添加了修改后的代码

      我将此添加到我的oncreate

      upgradeSecurityProvider();
      

      并修改了上下文的函数如下,仅此而已。不再有 SSL 连接问题

      private void upgradeSecurityProvider() {
              try{
                  ProviderInstaller.installIfNeededAsync(this, new ProviderInstaller.ProviderInstallListener() {
                      @Override
                      public void onProviderInstalled() {
                          Log.e("SSLFix", "New security provider installed.");
                      }
      
                      @Override
                      public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
                         // GooglePlayServicesUtil.showErrorNotification(errorCode, BuyersApp.this);
                          Log.e("SSLFix", "New security provider install failed.");
                      }
                  });
              }catch (Exception ex){
                  Log.e("SSLFix", "Unknown issue trying to install a new security provider", ex);
              }
      
      }
      

      仅此而已。

      【讨论】:

      • 两件事 1) 这样做是否安全。 2)我可以有工作代码吗?
      【解决方案6】:

      在我的案例中,我只是在 build.gradle(模块应用程序)中添加了一个依赖项并将代码添加到我的第一个活动中解决了我的问题。 依赖:

      implementation 'com.google.android.gms:play-services-base:11.0.0'
      

      我的代码:

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
              && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
          try {
              ProviderInstaller.installIfNeeded(this);
          } catch (GooglePlayServicesRepairableException e) {
              GooglePlayServicesUtil.showErrorNotification(e.getConnectionStatusCode(), this);
              return;
          } catch (GooglePlayServicesNotAvailableException e) {
              return;
          }
      }
      

      此问题出现在以下棒棒糖版本中。默认情况下 TLS 未启用,因此必须以编程方式启用。

      【讨论】:

        猜你喜欢
        • 2019-11-23
        • 2021-01-01
        • 2016-05-29
        • 1970-01-01
        • 2015-08-12
        • 2015-03-29
        • 2015-06-03
        • 2020-03-07
        • 1970-01-01
        相关资源
        最近更新 更多