Android 在 OKHttp 中启用 TLSv1.2

Posted

技术标签:

【中文标题】Android 在 OKHttp 中启用 TLSv1.2【英文标题】:Android Enable TLSv1.2 in OKHttp 【发布时间】:2015-05-28 18:25:21 【问题描述】:

我正在为我的项目使用 OKHttp。我想为我的服务调用启用 TLSv1.2。任何人都可以告诉我如何启用它。

【问题讨论】:

它对您有什么作用?请告诉我我不能在 *** 上提问 这个解决方案解决了我的问题:***.com/a/45853669/3448003 【参考方案1】:

见 OkHttp 的HTTPS documentation。

ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 
  .tlsVersions(TlsVersion.TLS_1_2)
  .cipherSuites( 
     CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
     CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
     CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
  .build();
OkHttpClient client = ... 
client.setConnectionSpecs(Collections.singletonList(spec));

【讨论】:

如文档所述 - 这仅适用于 android 5.0 或更高版本 它甚至不适用于 Android 5.0。我得到 SSL HandshakeException。 @ShanXeeshi 解释它是如何不起作用的?您的网络服务器未针对 TLS 1.2 配置可能是个问题。 现在我应该为下面的棒棒糖做些什么,因为它不起作用?我收到此异常 UnknownServiceException 无法找到可接受的协议。 isFallback=false, 模式=[ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, ....], tlsVersions=[TLS_1_2], supportsTlsExtensions=true)], 支持的协议=[SSLv3, TLSv1] 嗨@jesse-wilson,我试过了-不幸的是它对我不起作用ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) .allEnabledCipherSuites() .allEnabledTlsVersions() .build();我仍然有错误:Retrofit: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.【参考方案2】:

据我所知,OKHttp 不包含自己的 SSL/TLS 库,因此它只使用 Android 提供的标准 SSLSocket。

支持(和启用)哪些 TLS 版本取决于使用的 Android 版本。 在某些手机上支持 TLS 1.2,但默认情况下不启用(据我所知,这会影响 Android 4.1/4.2/4.4 的手机)。在这种情况下,您可以通过实现自定义包装器 SSLSocketFactory 来启用它,该包装器在内部使用默认 SSLSocketFactory 并在创建的每个 Socket 上调用 setEnabledProtocols(new String[]"TLS1.2")

在安装了 Google 服务的设备上,在旧的 Android 4.x 设备上启用 TLS 1.2 的首选方法是使用ProviderInstaller。

【讨论】:

感谢您的建议。 Ive combined it with https://***.com/a/38962842/5251437. If the ProviderInstaller` 失败(或抛出异常)我正在尝试使用自定义 SSLSoketFactory 来设置启用的协议...【参考方案3】:

检查我的代码!!完美运行!

private void checkTls() 
    if (android.os.Build.VERSION.SDK_INT < 21) 
        try 
            ProviderInstaller.installIfNeededAsync(this, new ProviderInstaller.ProviderInstallListener() 
                @Override
                public void onProviderInstalled() 
                

                @Override
                public void onProviderInstallFailed(int i, Intent intent) 
                
            );
         catch (Exception e) 
            finish();
            e.printStackTrace();
        
    

【讨论】:

在调用 Web 服务之前使用它。只需在您的应用中调用一次 此解决方案不适用于所有人。 ProviderInstaller 类来自 Google Play 服务,并非所有 Android 设备都提供 Google Play 服务。最值得注意的是在中国,不允许使用 GPS。【参考方案4】:

原来我的解决方案与 Ken 的解决方案非常相似(Java 除外)。我发现它here 虽然必须做一些小改动才能让它工作。希望这对其他人“开箱即用”有效。

public class TLSSocketFactoryCompat extends SSLSocketFactory 

private SSLSocketFactory internalSSLSocketFactory;

public TLSSocketFactoryCompat() throws KeyManagementException, NoSuchAlgorithmException 
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, null, null);
    internalSSLSocketFactory = context.getSocketFactory();


public TLSSocketFactoryCompat(TrustManager[] tm) throws KeyManagementException, NoSuchAlgorithmException 
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tm, new java.security.SecureRandom());
    internalSSLSocketFactory = context.getSocketFactory();


@Override
public String[] getDefaultCipherSuites() 
    return internalSSLSocketFactory.getDefaultCipherSuites();


@Override
public String[] getSupportedCipherSuites() 
    return internalSSLSocketFactory.getSupportedCipherSuites();


@Override
public Socket createSocket() throws IOException 
    return enableTLSOnSocket(internalSSLSocketFactory.createSocket());


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


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


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


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


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


private Socket enableTLSOnSocket(Socket socket) 
    if(socket != null && (socket instanceof SSLSocket)) 
        //Create list of supported protocols
        ArrayList<String> supportedProtocols = new ArrayList<>();
        for (String protocol : ((SSLSocket)socket).getEnabledProtocols()) 

            //Log.d("TLSSocketFactory", "Supported protocol:" + protocol);
            //Only add TLS protocols (don't want ot support older SSL versions)
            if (protocol.toUpperCase().contains("TLS")) 
                supportedProtocols.add(protocol);
            
        
        //Force add TLSv1.1 and 1.2 if not already added
        if (!supportedProtocols.contains("TLSv1.1")) 
            supportedProtocols.add("TLSv1.1");
        
        if (!supportedProtocols.contains("TLSv1.2")) 
            supportedProtocols.add("TLSv1.2");
        

        String[] protocolArray = supportedProtocols.toArray(new String[supportedProtocols.size()]);
        /*for (int i = 0; i < protocolArray.length; i++) 
            Log.d("TLSSocketFactory", "protocolArray[" + i + "]" + protocolArray[i]);
        */

        //enable protocols in our list
        ((SSLSocket)socket).setEnabledProtocols(protocolArray);
    
    return socket;
  

用法:

    OkHttpClient httpClient = new OkHttpClient();
    //Add Custom SSL Socket Factory which adds TLS 1.1 and 1.2 support for Android 4.1-4.4
    try 
        httpClient.setSslSocketFactory(new TLSSocketFactoryCompat());
     catch (KeyManagementException e) 
        e.printStackTrace();
     catch (NoSuchAlgorithmException e) 
        e.printStackTrace();
    

【讨论】:

我对此寄予厚望,但我看到了Enabling TLS 1.2 Enabling TLS 1.2 Enabling TLS 1.2 Enabling TLS 1.2 Enabling TLS 1.2 Enabling TLS 1.2 Enabling TLS 1.2 Enabling TLS 1.2 javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x5ff7f518: Failure in SSL library, usually a protocol error error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure (external/openssl/ssl/s3_pkt.c:1256 0x5fe90e28:0x00000003) at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:568) 在放弃之前,它至少会在几秒钟内尝试 enableTLSOnSocket() 8 次。 Someone Somewhere,我不确定你得到的错误是否真的相关(可能是证书问题),但它提醒我我更新了这个类以确保支持的协议列表是只添加到。我认为之前它可能只启用了 TLS 1.1 和 1.2。希望这会有所帮助。 嗨亚伦,我今天发现我看到的问题是由于设备的固件问题。如果底层固件刚刚损坏,该应用程序就无法发挥作用。 如何实现httpclient??我的意思是如何设置 setSslSocketFactory?【参考方案5】:

这与答案above 基本相同,但我觉得代码示例对于其他任何登陆这里并且在导航 java ssl 环境方面没有跟上速度的人都是有用的。

最终对我有用的是基于此处报告的问题:https://github.com/mattleibow/square-bindings/issues/1 从这个要点https://gist.github.com/mattleibow/c8abfa323db094b820cc

请注意,这些代码示例在 C#/Xamarin 中,但可以相当容易地转换为 java。

internal class CompleteSSLSocketFactory : SSLSocketFactory

    private readonly SSLSocketFactory innerFactory;

    public CompleteSSLSocketFactory (SSLSocketFactory innerFactory)
    
        this.innerFactory = innerFactory;
    

    public override string[] GetDefaultCipherSuites ()
    
        return innerFactory.GetDefaultCipherSuites ();
    

    public override string[] GetSupportedCipherSuites ()
    
        return innerFactory.GetSupportedCipherSuites ();
    

    public override Socket CreateSocket ()
    
        return MakeSocketSafe (innerFactory.CreateSocket ());
    

    public override Socket CreateSocket (Socket s, string host, int port, bool autoClose)
    
        return MakeSocketSafe (innerFactory.CreateSocket (s, host, port, autoClose));
    

    public override Socket CreateSocket (string host, int port)
    
        return MakeSocketSafe (innerFactory.CreateSocket (host, port));
    

    public override Socket CreateSocket (string host, int port, InetAddress localHost, int localPort)
    
        return MakeSocketSafe (innerFactory.CreateSocket (host, port, localHost, localPort));
    

    public override Socket CreateSocket (InetAddress host, int port)
    
        return MakeSocketSafe (innerFactory.CreateSocket (host, port));
    

    public override Socket CreateSocket (InetAddress address, int port, InetAddress localAddress, int localPort)
    
        return MakeSocketSafe (innerFactory.CreateSocket (address, port, localAddress, localPort));
    

    private Socket MakeSocketSafe (Socket socket)
    
        var sslSocket = socket as SSLSocket;
        if (sslSocket != null) 
            // enable all supported protocols for this socket
            sslSocket.SetEnabledProtocols (sslSocket.GetSupportedProtocols ());
            sslSocket.SetEnabledCipherSuites (sslSocket.GetSupportedCipherSuites ());
        
        return socket;
    

然后这样称呼它:

// this.client is an OkHttpClient
if (Android.OS.Build.VERSION.SdkInt < BuildVersionCodes.Lollipop) 
    this.client.SetSslSocketFactory(new CompleteSSLSocketFactory(HttpsURLConnection.DefaultSSLSocketFactory));

这对我有用,在 API 19 上测试。

【讨论】:

以上是关于Android 在 OKHttp 中启用 TLSv1.2的主要内容,如果未能解决你的问题,请参考以下文章

让 SSLEngine 在 Android (4.4.2) 上使用 TLSv1.2?

android okhttp超时怎么办

如何使用 OkHttp 启用证书固定

使用 Android WebViewClient 启用特定 SSL 协议

为Java Applet启用TLSv1.2

如何在Android应用程序中启用TLS 1.2支持(在Android 4.1 JB上运行)