如何在 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)的设备上启用它?

我曾经尝试创建一个自定义 SSLSocketFactory,它在创建 Socket时启用所有受支持的协议,然后使用我的自定义实现:

SetDefaultSSLSocketFactory (新的 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-0909:21:38.427: W/System.err (2496) : 例外: 异常: SSL 握手中止: SSL = 0xb7fa0620: SSL 库中的失败,通常是协议错误

03-0909:21:38.427: W/System.err (2496) : error: 14077410: SSL 例程: SSL23 _ GET _ SERVER _ HELLO: sslv3警告握手失败 (foreign/openssl/ssl/s23 _ clnt. c: 7410xa90e6990:0x0000000)

122465 次浏览

你应该用

 SSLContext.getInstance("TLSv1.2");

特定协议版本。

发生第二个异常是因为默认的 socketFactory 对失败使用了回退 SSLv3协议。

您可以从这里的主要答案中使用 NoSSLFactory 来抑制 如何为 HttpsUrlConnection 禁用 android 中的 SSLv3?

此外,您还应该使用所有证书(如果需要,可以使用客户端和受信任的证书)初始化 SSLContext

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

ProviderInstaller.installIfNeeded(getContext())

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

希望能有帮助。

启用 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
    Register (new Scheme (“ https”,new TlsSniSocketFactory () ,port)) ;

@ 固有的好奇-谢谢你发布这个。差不多了——您还需要向 SSLContext.init ()方法添加两个参数。

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

它将开始工作。再次感谢你张贴这一点。我解决了这个/我的问题与您的代码。

根据文章 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;
}
}

编辑: 感谢 ademar11190的 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 }


}

对于上面提到的答案,我有一些补充 它实际上是由杰西 · 威尔逊从 okhttp,square 给你提到的一次黑客攻击。根据这次黑客攻击,我不得不将我的 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();

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

这里的例子使用 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"});,以便 Socket 启用 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);

还有,记得把 build.gradle 中的 minSdkVersion提高到16

minSdkVersion 16

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

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