Как включить поддержку TLS 1.2 в Android-приложении (работает на Android 4.1 JB)
Согласно документам в Android для SSLSocket
и SSLContext
, протоколы TLS v1.1 и v1.2 поддерживаются в уровне API 16+, но по умолчанию не включены. 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
, а затем использовать мою собственную реализацию как:
- «Проверьте ошибку: num = 20» при подключении к gateway.sandbox.push.apple.com
- SSL, Tomcat и Grails
- Какие Cipher Suites включить для SSL Socket?
- Можно ли заставить Java игнорировать «хранилище доверия» и просто принять любой сертификат SSL, который он получает?
- Каков правильный способ безопасного отключения сокета SSL asio?
HttpsURLConnection.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-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)
- Как заставить SSL / https в Express.js
- Сколько накладных расходов накладывает SSL?
- Как разрешены имена серверов сертификатов SSL / Можно ли добавлять альтернативные имена с помощью keytool?
- Как получить файл .pem из .key и .crt файлов?
- Какие версии SSL / TLS поддерживают System.Net.WebRequest?
- Подключение SSL / Сброс соединения с помощью IISExpress
- веб-просмотр android с сертификатом клиента
- Динамические изображения, переданные через HTTPS, отображают сломанное изображение в слове при открытии HTTP-документа
2 способа включения TLSv1.1 и TLSv1.2:
- используйте это руководство: http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
- используйте этот class 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; } }
Изменить: Спасибо ademar111190 за реализацию kotlin ( ссылка )
class TLSSocketFactory constructor( private val internalSSLSocketFactory: SSLSocketFactory ) : SSLSocketFactory() { private val protocols = arrayOf("TLSv1.2", "TLSv1.1") override fun getDefaultCipherSuites(): Array = internalSSLSocketFactory.defaultCipherSuites override fun getSupportedCipherSuites(): Array = 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 } }
Вы должны использовать
SSLContext.getInstance("TLSv1.2");
для конкретной версии протокола.
Второе исключение произошло из-за того, что по умолчанию socketFactory использовал резервный протокол SSLv3 для сбоев.
Вы можете использовать NoSSLFactory из основного ответа здесь для его подавления. Как отключить SSLv3 в android для HttpsUrlConnection?
Также вы должны запустить SSLContext со всеми вашими сертификатами (клиентскими и доверенными, если они вам понадобятся)
Но все это бесполезно без использования
ProviderInstaller.installIfNeeded(getContext())
Более подробная информация приведена в правильном сценарии использования. https://developer.android.com/training/articles/security-gms-provider.html
Надеюсь, поможет.
У меня есть некоторые дополнения к вышеупомянутым ответам. Его infact взломать упоминается Джесси Уилсон от okhttp, площадь здесь . Согласно этому хаку, мне пришлось переименовать мою переменную SSLSocketFactory в
private SSLSocketFactory delegate;
Это мой class 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();
@Inherently Любопытный – спасибо за публикацию этого. Вы почти готовы – вам нужно добавить еще два параметра в метод SSLContext.init ().
TrustManager[] trustManagers = new TrustManager[] { new TrustManagerManipulator() }; sc.init(null, trustManagers, new SecureRandom());
он начнет работать. Еще раз большое спасибо за публикацию этого. Я решил эту проблему с вашим кодом.