WebView for Android< = 4.3中的TLS 1.1,1.2

时间:2016-04-30 06:45:37

标签: android ssl webview tls1.2

在我的Android应用程序中,我需要在WebView内显示第三方注册表单。不幸的是,我还需要支持Android版本< 4.3,连接到网站时出现SSL握手错误。但是,我能够使用自定义SSL上下文在Android 4.1+上创建直接请求,该上下文已明确启用TLS 1.1,但我无法将此SSL上下文传递到我的WebView。我尝试制作自定义WebViewClient

    private WebViewClient webViewClient = new WebViewClient() {
    @Override
    public void onPageFinished(WebView webView, String url) {
        if (presenter != null) {
            presenter.onLoadFinished();
        }
    }

    @Override
    public void onReceivedError(WebView webView,
                                WebResourceRequest request,
                                WebResourceError error) {
        if (presenter != null) {
            presenter.onLoadError();
        }
    }

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){
        handler.proceed();
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
                Request request = new Request.Builder().url(url).build();
    final Handler handler = new Handler(mContext.getMainLooper());
    //mOkHttpClient is an OkHttpClient with my custom SSLContext which has TLS 1.1 and TLS 1.2 enabled
    mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, final okhttp3.Response response) throws IOException {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            webView.loadDataWithBaseURL(
                                    null, response.body().string(), "text/html", "utf-8", null);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        });
    }

};

但由于shouldOverrideUrlLoading未在POST请求中调用,因此无法正常工作。

有没有办法让这项工作(可能是WebView的替代方案)?任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:4)

我不确定您是否可以使用当前的WebView,但是 我建议你看一下:https://crosswalk-project.org/documentation/android.html来替换WebView。

这是基于最新版本的Chrome和 你在用户运行的那个版本的Android上遇到了问题。

我确定还有其他选择,但这是我用于工作的那个,我知道工作。

祝你好运。

答案 1 :(得分:0)

某些版本不支持KitKat低于ssl和tsl的Android版本。

您可以通过黑客的方式来支持Kitkat以下的支持

1-)使用“ Retrofit或OkHttp”将页面加载为字符串,并将Webview加载为 字符串

implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'

功能

public void getHtml(){

        OkHttpClient client=new OkHttpClient();
        try {
            TLSSocketFactory tlsSocketFactory=new TLSSocketFactory();
            if (tlsSocketFactory.getTrustManager()!=null) {
                OkHttpClient.Builder builder = new OkHttpClient.Builder();
                builder.sslSocketFactory(tlsSocketFactory, tlsSocketFactory.getTrustManager());
                client = builder
                        .build();
            }
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }


       Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(columnist.getYaziAdress()+"/")
               .client(client)
               .addConverterFactory(ScalarsConverterFactory.create())
                .build();





        ApiEndPoint apiService =
                retrofit.create(ApiEndPoint.class);

        Call<ResponseBody> call = apiService.url(columnist.getYaziAdress());



        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                try {
                   // yazarText.loadDataWithBaseURL(null,response.body().string(),"text/html", "utf-8", null);
                    String a = response.body().string();

                    yazarText.loadData(a, "text/html; charset=utf-8", "UTF-8");
                    yazarText.getSettings().setUserAgentString("Android");
                    yazarText.getSettings().setUseWideViewPort(true);

                }catch (Exception e){

                }
            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {


                Toast.makeText(ArticleActivity.this,getString(R.string.ttsNull),Toast.LENGTH_LONG).show();

            }
        });
    }

TLSSocketFactory类

public class TLSSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory delegate;
    private TrustManager[] trustManagers;

    public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
        generateTrustManagers();
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, trustManagers, null);
        delegate = context.getSocketFactory();
    }

    private void generateTrustManagers() throws KeyStoreException, NoSuchAlgorithmException {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init((KeyStore) null);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:"
                    + Arrays.toString(trustManagers));
        }

        this.trustManagers = trustManagers;
    }


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

    @Nullable
    public X509TrustManager getTrustManager() {
        return  (X509TrustManager) trustManagers[0];
    }

}

Api终点

@GET()
Call<ResponseBody> url(@Url String url);

2-)使用Php脚本将html作为字符串返回到您的应用程序