android <= 4.3 的 WebView 中的 TLS 1.1、1.2

TLS 1.1, 1.2 in WebView for android <= 4.3

在我的 android 应用程序中,我需要在 WebView 中显示第 3 方注册表单。不幸的是,我还需要支持 android 版本 < 4.3,当您连接到该网站时会出现 SSL 握手错误。然而,我能够使用明确启用 TLS 1.1 的自定义 SSL 上下文在 android 4.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();
                        }
                    }
                });
            }
        });
    }

};

但这不起作用,因为 POST 请求未调用 shouldOverrideUrlLoading

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

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

这是建立在最新版本的 Chrome 和 使用 Android 用户 运行 的那个版本不会有问题。

我敢肯定还有其他选择,但这是我工作中使用的那个,我知道工作。

祝你好运。

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

你可以在hacking方式下支持Kitkat

1-)Load page as string with "Retrofit or OkHttp" and load webview as string

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 class

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 脚本 return html 作为应用程序的字符串