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

根据Android中SSLSocketSSLContext的文档,API级别16+支持TLS v1.1和v1.2协议,但默认情况下不启用。 http://developer.android.com/reference/javax/net/ssl/SSLSocket.html http://developer.android.com/reference/javax/net/ssl/SSLContext.html

如何在运行Android 4.1或更高版本(但低于5.0)的设备上启用它?

我已经尝试创build一个自定义SSLSocketFactory,它启用所有支持的协议,当创buildSocket时,以后使用我的自定义实现为:

HttpsURLConnection.setDefaultSSLSocketFactory(new MySSLSocketFactory());

 public class MySSLSocketFactory extends SSLSocketFactory { private SSLContext sc; private SSLSocketFactory ssf; public MySSLSocketFactory() { try { sc = SSLContext.getInstance("TLS"); sc.init(null, null, null); ssf = sc.getSocketFactory(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } } @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { SSLSocket ss = (SSLSocket) ssf.createSocket(s, host, port, autoClose); ss.setEnabledProtocols(ss.getSupportedProtocols()); ss.setEnabledCipherSuites(ss.getSupportedCipherSuites()); return ss; } @Override public String[] getDefaultCipherSuites() { return ssf.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return ssf.getSupportedCipherSuites(); } @Override public Socket createSocket(String host, int port) throws IOException, UnknownHostException { SSLSocket ss = (SSLSocket) ssf.createSocket(host, port); ss.setEnabledProtocols(ss.getSupportedProtocols()); ss.setEnabledCipherSuites(ss.getSupportedCipherSuites()); return ss; } @Override public Socket createSocket(InetAddress host, int port) throws IOException { SSLSocket ss = (SSLSocket) ssf.createSocket(host, port); ss.setEnabledProtocols(ss.getSupportedProtocols()); ss.setEnabledCipherSuites(ss.getSupportedCipherSuites()); return ss; } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { SSLSocket ss = (SSLSocket) ssf.createSocket(host, port, localHost, localPort); ss.setEnabledProtocols(ss.getSupportedProtocols()); ss.setEnabledCipherSuites(ss.getSupportedCipherSuites()); return ss; } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { SSLSocket ss = (SSLSocket) ssf.createSocket(address, port, localAddress, localPort); ss.setEnabledProtocols(ss.getSupportedProtocols()); ss.setEnabledCipherSuites(ss.getSupportedCipherSuites()); return ss; } } 

但是,在尝试与启用了TLS 1.2的服务器build立连接时,它仍然会发生exception。

这是我得到的例外:

03-09 09:21:38.427:W / System.err(2496):javax.net.ssl.SSLHandshakeException:javax.net.ssl.SSLProtocolException:SSL握手中止:ssl = 0xb7fa0620:SSL库中的失败,通常是协议错误

03-09 09:21:38.427:W / System.err(2496):错误:14077410:SSL例程:SSL23_GET_SERVER_HELLO:sslv3警报握手故障(external / openssl / ssl / s23_clnt.c:741 0xa90e6990:0x00000000)

Solutions Collecting From Web of "如何在Android应用程序(在Android 4.1 JB上运行)中启用TLS 1.2支持"

2种启用TLSv1.1和TLSv1.2的方法:

  1. 使用本指南: http : //blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
  2. 使用这个类https://github.com/erickok/transdroid/blob/master/app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java
    schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), port));

我按照http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2中提供的说明解决了这个问题/几乎没有变化。

 SSLContext context = SSLContext.getInstance("TLS"); context.init(null, null, null); SSLSocketFactory noSSLv3Factory = null; if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { noSSLv3Factory = new TLSSocketFactory(sslContext.getSocketFactory()); } else { noSSLv3Factory = sslContext.getSocketFactory(); } connection.setSSLSocketFactory(noSSLv3Factory); 

这是自定义TLSSocketFactory的代码:

 public static class TLSSocketFactory extends SSLSocketFactory { private SSLSocketFactory internalSSLSocketFactory; public TLSSocketFactory(SSLSocketFactory delegate) throws KeyManagementException, NoSuchAlgorithmException { internalSSLSocketFactory = delegate; } @Override public String[] getDefaultCipherSuites() { return internalSSLSocketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return internalSSLSocketFactory.getSupportedCipherSuites(); } @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)); } /* * Utility methods */ private static Socket enableTLSOnSocket(Socket socket) { if (socket != null && (socket instanceof SSLSocket) && isTLSServerEnabled((SSLSocket) socket)) { // skip the fix if server doesn't provide there TLS version ((SSLSocket) socket).setEnabledProtocols(new String[]{TLS_v1_1, TLS_v1_2}); } return socket; } private static boolean isTLSServerEnabled(SSLSocket sslSocket) { System.out.println("__prova__ :: " + sslSocket.getSupportedProtocols().toString()); for (String protocol : sslSocket.getSupportedProtocols()) { if (protocol.equals(TLS_v1_1) || protocol.equals(TLS_v1_2)) { return true; } } return false; } } 

你应该使用

  SSLContext.getInstance("TLSv1.2"); 

对于特定的协议版本。

第二个exception发生,因为默认的socketFactory使用故障回退SSLv3协议。

您可以使用NoSSLFactory从主要的答案在这里它的抑制如何禁用Android的HttpsUrlConnection的SSLv3?

你也应该用你所有的证书(客户端和可信任的证书)来初始化SSLContext,

但是,所有这些都是没用的

 ProviderInstaller.installIfNeeded(getContext()) 

这里有更多的信息与正确的使用场景https://developer.android.com/training/articles/security-gms-provider.html

希望能帮助到你。

我对上面提到的答案有一些补充它实际上是由Jesh Wilson从okhttp 在这里提到的一个黑客。 根据这个黑客,我不得不重新命名我的SSLSocketFactoryvariables

 private SSLSocketFactory delegate; 

请注意,如果您提供委托以外的任何名称,则会发生错误。 Iam在下面发布我的完整解决scheme

这是我的TLSSocketFactory

 public class TLSSocketFactory extends SSLSocketFactory { private SSLSocketFactory delegate; public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { SSLContext context = SSLContext.getInstance("TLS"); context.init(null, null, null); delegate = context.getSocketFactory(); } @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; } } 

这就是我用okhttp和改造的方式

  OkHttpClient client=new OkHttpClient(); try { client = new OkHttpClient.Builder() .sslSocketFactory(new TLSSocketFactory()) .build(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } Retrofit retrofit = new Retrofit.Builder() .baseUrl(URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); 

@本能地好奇 – 感谢张贴这个。 你几乎在那里 – 你必须添加两个参数到SSLContext.init()方法。

 TrustManager[] trustManagers = new TrustManager[] { new TrustManagerManipulator() }; sc.init(null, trustManagers, new SecureRandom()); 

它将开始工作。 再次非常感谢你张贴这个。 我用你的代码解决了这个问题。