【问题标题】:Is it possible to create Android app based on webview accessing the https urls是否可以基于访问 https url 的 webview 创建 Android 应用程序
【发布时间】:2016-08-10 05:02:18
【问题描述】:

我有一个类使用 webview 打开这个 url:https://eaadhaar.uidai.gov.in 并且我已经使用下面的代码创建了本地证书。我成功地得到了 200 的响应,但我的问题是我如何继续显示网页用户。 我已经测试了这段代码:

public class WebViewFragment extends Fragment {

    public final String TAG = WebViewFragment.class.getSimpleName();
    private WebView webView;

    public static final int DEFAULT_BUFFER_SIZE = 2048;
    public static final String DEFAULT_CHARSET_NAME = "UTF-8";

    public WebViewFragment() {

    }

    public static WebViewFragment newInstance() {
        return new WebViewFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.layout_webview_fragment, container, false);

        initGlobal(view);
        return view;
    }

    private void initGlobal(View view) {
        webView = (WebView) view.findViewById(R.id.webview);
        //MyBrowser is a custom class which extends Webviewclient which loads the given url in the webview
        webView.setWebViewClient(new MyBrowser());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setDomStorageEnabled(true);

        webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

//        webView.loadUrl("http://www.google.com");
//        webView.loadUrl("https://www.github.com");
//        webView.loadUrl("https://eaadhaar.uidai.gov.in/");

        try {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

            StrictMode.setThreadPolicy(policy);

            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream caInput = new BufferedInputStream(getActivity().getResources().openRawResource(R.raw.newcertificate));
            Certificate ca;
            try {
                ca = cf.generateCertificate(caInput);
                System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
            } finally {
                caInput.close();
            }

            // Create a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);


            // Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);

            // Create a SSLContext with the certificate
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);

            // Create a HTTPS connection
            URL url = new URL("https://eaadhaar.uidai.gov.in");
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(sslContext.getSocketFactory());

            InputStream in = conn.getInputStream();
            int lastResponseCode = conn.getResponseCode();
            Log.e(TAG, "response code=" + lastResponseCode);

            if (lastResponseCode == 200) {
                Toast.makeText(getActivity(), "Response code==" + lastResponseCode, Toast.LENGTH_SHORT).show();
            }
            copyInputStreamToOutputStream(in, System.out, 2048, true, true);


        } catch (Exception e) {
            Log.e(TAG, "Exception========" + e.toString());
        }
    }

    public void copyInputStreamToOutputStream(InputStream from, OutputStream to, int bufferSize, boolean closeInput, boolean closeOutput) {
        try {
            int totalBytesRead = 0;
            int bytesRead = 0;
            int offset = 0;
            byte[] data = new byte[bufferSize];

            while ((bytesRead = from.read(data, offset, bufferSize)) > 0) {
                totalBytesRead += bytesRead;
                to.write(data, offset, bytesRead);
                Log.e(TAG, "Copied " + totalBytesRead + " bytes");
            }
            closeStreams(from, to, closeInput, closeOutput);
        } catch (Exception e) {
            closeStreams(from, to, closeInput, closeOutput);
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    public void closeStreams(InputStream from, OutputStream to, boolean closeInput, boolean closeOutput) {
        try {
            if (to != null)
                to.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            if (closeInput && from != null)
                from.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            if (closeOutput && to != null)
                to.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

【问题讨论】:

  • 您的具体问题是什么?是“如何在 UI 中显示 webview”还是“webview 可以加载 HTTPS”还是“webview 可以加载 HTTPS 页面,其证书不受操作系统信任,我希望它使用自定义 CA 存储验证证书”?
  • @jakub.g 实际上我只是想在 webview 中加载上面的 url 并显示空白屏幕。我找到了使用 onReceivedSslError 方法绕过这个问题的任何方法。我被误导了,我还需要自定义密钥库来访问 url。
  • 如果显示空白屏幕,则很可能由于某种原因无法验证 https 证书。请注意,您不应该在生产应用程序中只使用 onReceivedSslError 中的 handler.proceed(),因为这违背了 HTTPS 的目的,因为它使应用程序容易受到中间人攻击。
  • @jakub.g 但我没有调用任何 https 网络服务,只是通过向用户显示一个对话框来打开 url ?我只有在 stackoverflow 上才有这个解决方案
  • 如果您显示对话框,总比盲目接受无效证书要好。但是由于某种原因,https 证书验证仍然失败:1)设备没有正确的 CA 证书,或者 2)您的证书无效(可能是自签名等)。页面在 Chrome 等普通浏览器中加载是否正常?如果是这样,最有可能的问题是服务器没有发送完整的证书链。 ssllabs.com/ssltest/…

标签: https android-webview x509certificate httpsurlconnection sslsocketfactory


【解决方案1】:

问题很可能是服务器没有发送完整的证书链(不发送中间证书)

看这里:

https://www.ssllabs.com/ssltest/analyze.html?d=eaadhaar.uidai.gov.in&latest

点击Certification Paths,你会在GeoTrust SSL CA - G3旁边看到Extra download

这是一个特定于 WebView 的东西:它不获取中间证书(Chrome 或 Firefox 等常规浏览器要么这样做,要么缓存他们在用户浏览其他页面时看到的证书)

您需要联系服务器管理员/操作员以发送所有中间证书。 我们的应用也遇到过类似问题,通过在 SSL 握手中发送所有中间证书来解决。

覆盖onReceivedSslError 是一种不安全的解决问题的方法,不应在生产应用中使用。将其视为仅用于开发的工具。

【讨论】:

    猜你喜欢
    • 2019-05-02
    • 2011-03-21
    • 2019-12-11
    • 1970-01-01
    • 2017-11-26
    • 1970-01-01
    • 2010-12-24
    • 2013-03-06
    • 2019-02-18
    相关资源
    最近更新 更多