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


71

根据Android forSSLSocket和中的文档,SSLContextAPI级别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)的设备上启用它?

我尝试创建一个自定义SSLSocketFactory,该SSLSocketFactorySocket在创建时启用所有受支持的协议,以后将我的自定义实现用作:

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的服务器建立连接时,它仍然会出现异常。

这是我得到的异常:

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警报握手失败(外部/openssl/ssl/s23_clnt.c:741 0xa90e6990:0x00000000)


1
在我的测试中,我发现虽然TLSv1.2可用并且可以在API 16-18上启用,但是特定用途仍然存在问题,例如提供客户端证书,甚至包括Play Services之后。尚无解决方案。
hooby3dfx

1
更令人困惑的是,SSLEngine状态仅在API20 +中支持TLS1.1 / 1.2。developer.android.com/reference/javax/net/ssl/SSLEngine.html
David Thornley,2016年

Answers:


20

启用TLSv1.1和TLSv1.2的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));

我原样使用了您的第二个建议,但我遇到了问题。在TlsSniSocketFactory类的createSocket()方法中,返回的SSL会话无效。我不知道为什么,但是这在API 17和API 24设备中正在发生?请看看我对此的详细查询:stackoverflow.com/questions/40684898/…–
LoveForDroid

非常感谢!这节省了我很多时间!
卡斯珀·吉林斯

使用第一个选项,使用httpclient但变老了v1.0,实际上想要v1.2。私有HttpClient getTheHttpClient(HttpParams httpParameters){SchemeRegistry注册表= new SchemeRegistry(); 尝试{Registry.register(new Scheme(“ https”,new TLSSocketFactory(),443)); } catch(KeyManagementException e){e.printStackTrace(); } catch(NoSuchAlgorithmException e){e.printStackTrace(); } HttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager(httpParameters,Registry),httpParameters); 返回客户;}
Horrorgoogle '19

13

我按照文章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;
    }
}

编辑:感谢ademar111190的kotlin实现(链接

class TLSSocketFactory constructor(
        private val internalSSLSocketFactory: SSLSocketFactory
) : SSLSocketFactory() {

    private val protocols = arrayOf("TLSv1.2", "TLSv1.1")

    override fun getDefaultCipherSuites(): Array<String> = internalSSLSocketFactory.defaultCipherSuites

    override fun getSupportedCipherSuites(): Array<String> = internalSSLSocketFactory.supportedCipherSuites

    override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose))

    override fun createSocket(host: String, port: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))

    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort))

    override fun createSocket(host: InetAddress, port: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))

    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int) =
            enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort))

    private fun enableTLSOnSocket(socket: Socket?) = socket?.apply {
        if (this is SSLSocket && isTLSServerEnabled(this)) {
            enabledProtocols = protocols
        }
    }

    private fun isTLSServerEnabled(sslSocket: SSLSocket) = sslSocket.supportedProtocols.any { it in protocols }

}

2
嗨,它对我没有用,知道为什么吗?如果您能帮助的话,那就太好了
Anand Sonawane

我已经用您的答案来建立MQTT SSL连接,它工作得很好!谢谢。#UP
Stoica Mircea

@StoicaMircea这实际上将不使用ssl进行连接。这不安全。:您可以在这里用我的解决办法stackoverflow.com/questions/47410689/...
阿米尔Ziarati

2
@AmirZiarati,我后来发现你是对的。我也从切换{TLS_v1_1, TLS_v1_2}{TLS_v1_2, TLS_v1_1},首先尝试使用TLS 1.2
Stoica Mircea

好点;)谢谢。请根据您的建议编辑我的答案。
Amir Ziarati

8

play-services-safetynet在android中添加库build.gradle

implementation 'com.google.android.gms:play-services-safetynet:+'

并将此代码添加到您的MainApplication.java

@Override
  public void onCreate() {
    super.onCreate();
    upgradeSecurityProvider();
    SoLoader.init(this, /* native exopackage */ false);
  }

  private void upgradeSecurityProvider() {
    ProviderInstaller.installIfNeededAsync(this, new ProviderInstallListener() {
      @Override
      public void onProviderInstalled() {

      }

      @Override
      public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
//        GooglePlayServicesUtil.showErrorNotification(errorCode, MainApplication.this);
        GoogleApiAvailability.getInstance().showErrorNotification(MainApplication.this, errorCode);
      }
    });
  }

1
这解决了我的问题...错误:14077410:SSL例程:SSL23_GET_SERVER_HELLO:sslv3警报握手失败
Mridul Das


7

你应该用

 SSLContext.getInstance("TLSv1.2"); 

用于特定的协议版本。

发生第二个异常是因为默认的socketFactory使用后备SSLv3协议来失败。

您可以在此处使用主要答案中的NoSSLFactory进行抑制如何在android中为HttpsUrlConnection禁用SSLv3?

另外,您还应该使用所有证书(如果需要,则使用客户端证书和受信任证书)来初始化SSLContext。

但是如果不使用,所有这些都是无用的

ProviderInstaller.installIfNeeded(getContext())

这是有关正确使用情况的更多信息,https://developer.android.com/training/articles/security-gms-provider.html

希望能帮助到你。


2
根据这一点- grepcode.com/file/repo1.maven.org/maven2/org.ektorp/... -只是"TLS""SSL""SSLV2"被接受。
IgorGanapolsky '16

这似乎无法解决4.1.2客户端的问题
坦率地说,

也许这很愚蠢,但是我尝试将4.0.1.2版本的com.google.android lib添加到我的gradle中,而我无法导入com.google.android.gms.security。*那么,您是从哪个包中导入ProviderInstaller的呢?
普热显克微支

6

我对上述答案有一些补充,实际上是okhttp的Jesse Wilson提到的hack,这里是。根据此hack,我不得不将我的SSLSocketFactory变量重命名为

private SSLSocketFactory delegate;

这是我的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();

1
这是我得到的最有帮助的答案。多谢兄弟。
Njeru Cyrus

0

@Inherently Curious-感谢发布。差不多了-您必须再向SSLContext.init()方法添加两个参数。

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

它将开始工作。再次非常感谢您发布此信息。我用您的代码解决了这个/我的问题。


1
我一定错过了,但是TrustManagerManipulator类从哪里来?
truedat101 '18

0

正如OP所说,API级别16+支持TLS v1.1和v1.2协议,但默认情况下未启用TLS,我们只需要启用它即可。

此处的示例使用HttpsUrlConnection,而不是HttpUrlConnection。跟随https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/,我们可以创建一个工厂

class MyFactory extends SSLSocketFactory {

    private javax.net.ssl.SSLSocketFactory internalSSLSocketFactory;

    public MyFactory() throws KeyManagementException, NoSuchAlgorithmException {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, null, null);
        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)) {
            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
        }
        return socket;
    }
}

无论您使用哪个网络库,都要确保((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});被调用,以便套接字启用了TLS协议。

现在,您可以在 HttpsUrlConnection

class MyHttpRequestTask extends AsyncTask<String,Integer,String> {

    @Override
    protected String doInBackground(String... params) {
        String my_url = params[0];
        try {
            URL url = new URL(my_url);
            HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection();
            httpURLConnection.setSSLSocketFactory(new MyFactory());
            // setting the  Request Method Type
            httpURLConnection.setRequestMethod("GET");
            // adding the headers for request
            httpURLConnection.setRequestProperty("Content-Type", "application/json");


            String result = readStream(httpURLConnection.getInputStream());
            Log.e("My Networking", "We have data" + result.toString());


        }catch (Exception e){
            e.printStackTrace();
            Log.e("My Networking", "Oh no, error occurred " + e.toString());
        }

        return null;
    }

    private static String readStream(InputStream is) throws IOException {
        final BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("US-ASCII")));
        StringBuilder total = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            total.append(line);
        }
        if (reader != null) {
            reader.close();
        }
        return total.toString();
    }
}

例如

new MyHttpRequestTask().execute(myUrl);

另外,请记住将minSdkVersionbuild.gradle设置为16

minSdkVersion 16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.