Android Web View с поддержкой TLS 1.2 - PullRequest
0 голосов
/ 28 июня 2019

У меня работает приложение для Android, которое должно работать только на устройстве Android 4.2.2.

Приложение имеет веб-просмотр. В этом веб-представлении я загружаю веб-сайт, который размещен на сервере Firebase.

  1. Сайт защищен HTTPS
  2. Приложение использует WebViewClient для установления SSL-соединений с сервером.
  3. Соединение с этим сайтом зашифровано и аутентифицировано с использованием TLS. 1.2, ECDHE_RSA с X25519 и AES_128_GCM.


  1. Я добавил TLSScoketFactory для включения TLS1.2. По умолчанию, которая не включена в версии 4.2.2.
  2. Я использую okhttp клиент для запроса.
public class TLSSocketFactory extends SSLSocketFactory {

    private static final String TAG = "TLSSocketFactory";

    private SSLSocketFactory internalSSLSocketFactory;

    public TLSSocketFactory() {
        internalSSLSocketFactory = getSslContext().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)  && isTLSServerEnabled((SSLSocket) socket) ) {
            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
        }
        return socket;
    }


    public static X509TrustManager getTrustManager() {
        return new X509TrustManager() {
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[] {};
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
        };
    }

    public static SSLContext getSslContext() {
        try {
            SSLContext sslContext =  SSLContext.getInstance("TLS");
            sslContext.init(null, null, null);
            return sslContext;
        } catch (Exception ex) {
            Log.e(TAG, "Problem while getting SslContext", ex);
            throw new RuntimeException(ex);
        }
    }
}
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private static final String HTTP = "http://";
    private static final String HTTPS = "https://";
    private static final String CSS_FILE = ".css";
    private static final String JS_FILE = ".js";
    private static final String CSS_DIR = "/css/";
    private static final String JS_DIR = "/js/";

    private WebView webView;
    private ImageView button;
    private EditText urlView;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressBar = (ProgressBar) findViewById(R.id.progress_bar);
        progressBar.getProgressDrawable().setColorFilter(getResources().getColor(R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN);

        urlView = (EditText) findViewById(R.id.url);

        webView = (WebView) findViewById(R.id.web_view);
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        webView.setWebChromeClient(buildWebChromeClient());
        webView.setWebViewClient(buildClient());

        webView.post(new Runnable() {
            @Override
            public void run() {
                webView.loadUrl("https://sample-project.firebaseapp.com");
            }
        });

        button = (ImageView) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onButtonClick();
            }
        });
    }



    private WebViewClient buildClient() {
        return new WebViewClient() {

           private OkHttpClient okHttp = new OkHttpClient.Builder().sslSocketFactory(new TLSSocketFactory(), TLSSocketFactory.getTrustManager()).build();



            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                Log.e(TAG, "URL -> " + url);

             /*   if (url.contains(CSS_FILE) || url.contains(JS_FILE) || url.contains(CSS_DIR) || url.contains(JS_DIR)) {
                    Log.e(TAG, "Css or js -> default interceptor");
                    return super.shouldInterceptRequest(view, url);
                } else {*/
                    Request okHttpRequest = new Request.Builder().url(url).header("User-Agent", "OkHttp Example").build();
                    try {
                        Response response = okHttp.newCall(okHttpRequest).execute();
                        return new WebResourceResponse("text/html", "UTF-8", response.body().byteStream());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return null;
           //     }
            }

            public void onPageFinished(WebView view, String url) {
                Log.i(TAG, "Finished loading URL: " + url);
                super.onPageFinished(view, url);
             //   webView.loadUrl(url);
            }

            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
              //  MessageDialogFragment.getInstance(R.string.information, getString(R.string.oh_no) + description).show(getSupportFragmentManager(), "MessageDialogFragment");
                Log.i(TAG, "failing URL: " + failingUrl);
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                handler.proceed(); // Ignore SSL certificate errors
            }
        };
    }

    private WebChromeClient buildWebChromeClient() {
        return new WebChromeClient() {
            public void onProgressChanged(WebView view, int progress) {
                if (progress < 100 && progressBar.getVisibility() == ProgressBar.GONE) {
                    progressBar.setVisibility(ProgressBar.VISIBLE);
                }

                progressBar.setProgress(progress);
                if (progress == 100) {
                    progressBar.setVisibility(ProgressBar.GONE);
                }
            }

            @Override
            public boolean onConsoleMessage(ConsoleMessage m) {
                switch (m.messageLevel()) {
                    case LOG:
                        Log.i(TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
                        return true;
                    case TIP:
                        Log.v(TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
                        return true;
                    case DEBUG:
                        Log.d(TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
                        return true;
                    case WARNING:
                        Log.w(TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
                        return true;
                    case ERROR:
                        Log.e(TAG, String.format("%s (%s:%d)", m.message(), m.sourceId(), m.lineNumber()));
                        return true;
                }
                return false;
            }

        };
    }

    private void onButtonClick() {
        KeyboardManager.hideKeyboard(this, getCurrentFocus());

        if(NetworkManager.isNetworkEnable(this)) {
            String url = urlView.getText().toString();
            if (StringUtils.isNotBlank(url)) {
                if(!(url.startsWith(HTTP) || url.startsWith(HTTPS))){
               //     url = HTTP + url;
                }
              //  webView.loadUrl(url);
            } else {
                MessageDialogFragment.getInstance(R.string.information, getString(R.string.wrong_url)).show(getSupportFragmentManager(), "MessageDialogFragment");
            }
        } else {
            MessageDialogFragment.getInstance(R.string.information, getString(R.string.no_network)).show(getSupportFragmentManager(), "MessageDialogFragment");
        }
    }

}

Проблема, с которой я сталкиваюсь при попытке загрузить URL-адрес веб-сайта в WebView:

E / хром: внешний / хром / сеть / сокет / ssl_client_socket_openssl.cc: 792: [0102/102633: ОШИБКА: ssl_client_socket_openssl.cc (792)] сбой квитирования; возвращается -1, код ошибки SSL 1, net_error -107

...