Включение определенных SSL-протоколов с помощью Android WebViewClient
Мое приложение использует WebViewClient для подключения SSL к серверу.
Сервер настроен только для приема протоколов TLSv1.1 и выше.
1) Как проверить, какие SSL-протоколы являются a) Поддерживается и b) Включено по умолчанию при использовании Android WebViewClient на устройстве.
2) Как включить определенные SSL-протоколы для экземпляра Android WebViewClient, который используется в моем приложении.
На одном из тестовых устройств под управлением Android 4.3,
WebViewClient запускает обратный вызов onReceivedError с описанием "Не удалось выполнить SSL-квитирование"
Журналы Chrome следующие:
01-29 15: 58: 00.073 5486 5525 W chromium_net: external/chromium/net/http/http_stream_factory_impl_job.cc: 865: [0129/155800: ПРЕДУПРЕЖДЕНИЕ: http_stream_factory_impl_job.cc(865)] Возврат к SSLv3, поскольку хост является нетерпимым TLS: 10.209.126.125:443 01-29 15: 58: 00.083 5486 5525 E chromium_net: external/chromium/net/socket/ssl_client_socket_openssl.cc: 792: [0129/155800: ERROR: ssl_client_socket_openssl.cc(792)] не выполнено; 0, код ошибки SSL 5, net_error -107
Мое приложение также использует классы HttpClient и HttpsUrlConnection для настройки SSL-соединений. Я смог использовать SSLSocket API для включения определенных протоколов при использовании этих классов.
http://developer.android.com/reference/javax/net/ssl/SSLSocket.html#setEnabledProtocols(java.lang.String[])
Мне нужно сделать то же самое с WebViewClient.
Ответы
Ответ 1
В соответствии с документацией невозможно поддерживать TLS 1.0 в WebView в Android < 4,3. Для Android 4.4 он по умолчанию отключен.
Проверьте эту таблицу для поддержки TLS 1.0 в разных браузерах: https://en.wikipedia.org/wiki/Transport_Layer_Security#Web_browsers
Ответ 2
Если ваше приложение использует или вы хотите использовать службы Google Play, вы можете использовать более новые функции безопасности на старых телефонах, установив их Provider
. Он прост в установке, только одна строка (плюс обработка исключений и т.д.). Вам также нужно будет добавить сервисы google play к вашему файлу gradle, если у вас его еще нет. ProviderInstaller
включен в пакет -base
.
try {
ProviderInstaller.installIfNeeded(this);
} catch (GooglePlayServicesRepairableException e) {
// Fix it
} catch (GooglePlayServicesNotAvailableException e) {
// Skip it
}
Полный пример см. в "Обновление вашего поставщика безопасности для защиты от SSL-эксплойтов" от Google.
Ответ 3
На самом деле мне удалось заставить его работать, но для этого вам нужна библиотека okHttp.
Попробуйте это, когда вы настраиваете активность браузера:
WebViewClient client = new WebViewClient() {
private OkHttpClient okHttp = new OkHttpClient.Builder().build();
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Request okHttpRequest = new Request.Builder().url(url).build();
try {
Response response = okHttp.newCall(okHttpRequest).execute();
return new WebResourceResponse(response.header("Content-Type", "plain/text"), response.header("Content-Encoding", "deflate"), response.body().byteStream());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
webView.setWebViewClient(client);
Кроме того, вам понадобится классический Манипулятор Trust Manager, SSL-сокет factory и его реализация в вашем классе Application:
public class TrustManagerManipulator implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] acceptedIssuers = new X509Certificate[] {};
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
public static void allowAllSSL()
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[] { new TrustManagerManipulator() };
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return acceptedIssuers;
}
}
SSl Socket Factory:
public class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
TrustManager[] managers = new TrustManager[] { new TrustManagerManipulator() };
context.init(null, managers, new SecureRandom());
internalSSLSocketFactory = context.getSocketFactory();
}
@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));
}
private Socket enableTLSOnSocket(Socket socket) {
if(socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
}
return socket;
}
}
Класс приложения:
public class App extends Application {
private static App appInstance;
@Override
public void onCreate() {
super.onCreate();
setupSSLconnections();
}
private void setupSSLconnections() {
try {
HttpsURLConnection.setDefaultSSLSocketFactory(new TLSSocketFactory());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}