2015-02-04 139 views
22

我的應用程序使用WebViewClient與服務器建立SSL連接。 服務器被配置爲只接受TLSv1.1及以上協議。使用Android WebViewClient啓用特定的SSL協議

1)如何檢查哪些SSL協議是a)支持的和b)默認情況下在設備上使用Android WebViewClient時啓用。
2)如何爲我的應用程序中使用的Android WebViewClient實例啓用特定的SSL協議。

在運行Android 4.3, WebViewClient投用描述onReceivedError回調 「無法執行SSL握手」 測試設備之一
鉻日誌如下:
01-29 15:58:00.073 5486 5525W¯¯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:錯誤: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

AFAIK啓用的協議WebView不能被應用程序卡住。支持或不支持哪些協議取決於Android版本。對於TLS 1.1及更高版本,您需要安卓4.4或更高版本(請參閱[這裏](https://www.ssllabs.com/ssltest/viewClient.html?名稱= Android和版本= 4.3))。 – Robert

+0

有沒有人知道這一點?我正在開發一個項目,目前我們需要從三個非生產測試服務器的TLS v1.2回退到1.1。我們試圖使用這樣的東西: adb shell'echo「browser --show-fps-counter --ssl-version -min = tls1.1 --ssl-version-max = tls1.1」>/data/local/tmp/webview-command-line' FPS計數器在Android L平板電腦上顯示,但不在Android TV上顯示(這很奇怪,因爲它們應該是相同的Web視圖代碼)。 ssl版本標誌似乎不起作用;從設備捕獲的tcpdump仍顯示正在使用TLS v1.2。 任何幫助將是偉大的。 – cynod

+0

@ user802467您是否找到針對該問題的解決方案?我也面臨同樣的問題 –

回答

3

如果您的應用正在使用,或者您願意使用Google Play服務,則可以通過安裝Provider,在較舊的手機上使用較新的安全功能。它很容易安裝,只有一行(加上異常處理等)。如果您尚未擁有它,您還需要將Google Play服務添加到您的Gradle文件中。 ProviderInstaller包含在-base包中。

try { 
    ProviderInstaller.installIfNeeded(this); 
} catch (GooglePlayServicesRepairableException e) { 
    // Fix it 
} catch (GooglePlayServicesNotAvailableException e) { 
    // Skip it 
} 

有關完整的示例,請參閱Google的"Updating Your Security Provider to Protect Against SSL Exploits"

+1

注意:這是在主線程(UI線程)上調用的同步調用。您可以在後臺運行的SyncAdapter或AsyncTask中執行此調用以執行此更新嘗試或使用此調用的installIfNeededAsync版本進行調用以避免阻止主線程 –

+2

而且即使在成功安裝提供程序之後,您也需要在[16-20]之間在Android版本中提供https請求時明確啓用TLS 1.2/TLS 1.1。我在KITKAT設備上驗證了這一點。 – ua741

+1

注意:這似乎無法解決4.1.2客戶端的問題。 – frank

2

其實,我設法使它工作,但你需要爲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); 

此外,你需要經典的信託經理機械臂,SSL套接字工廠及其實施中的應用程序類:

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套接字工廠:

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

App類:

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(); 
     } 
    } 
} 
+1

不幸的是,我只在我的webView中看到純文本;它可能取決於javaScript的執行。還發現這篇文章https://artemzin.com/blog/android-webview-io/ –

+0

@ ar-g你解決了你的問題嗎?請告訴我,如果你做到了。 –

+0

@AlexanderZarovniy問題出現在第三方服務中,我們對4.4以下的android使用不同的方案(與android無關) –