【问题标题】:Can't access website from Android app after HTTPS certificate renewingHTTPS 证书更新后无法从 Android 应用程序访问网站
【发布时间】:2019-08-08 15:49:11
【问题描述】:

我有一个使用来自外部服务器(我无法控制)的网络服务的 Android 应用。最近该服务器未能更新其 HTTPS 证书,并且有几个小时不可用。在此时间间隔内,我的应用程序的一些用户尝试使用这些服务,这自然失败了。 问题是,现在问题已在服务器上得到解决,这些用户仍然无法从我的应用程序访问该网站。一位用户甚至无法从其移动设备的浏览器访问该网站,另一位用户仅在尝试使用我的应用程序时被阻止。

我在更新 HTTPS 证书方面的经验有限,所以我想知道可能出了什么问题?似乎这些设备已将过期证书保存在缓存中,并且不使用新证书。重新安装我的应用并不能解决问题。

谢谢

【问题讨论】:

  • 如果您的应用启用了网络安全配置,请确保您的新证书包含在您设置的任何网络安全配置规则中。然而,“甚至无法从他的移动设备的浏览器访问网站”问题表明服务器团队可能选择了一个非传统的证书颁发机构,并非所有 Android 版本都必须承认。
  • 谢谢。那么如果应用程序网络安全配置出现问题,所有用户都会受到影响,对吧?我会问他们是否更改了证书颁发机构。
  • "如果应用网络安全配置出现问题,所有用户都会受到影响,对吧?" -- 仅限Android 7.0 及更高版本,因为在此之前没有网络安全配置。但是,是的,在那个子集中,我预计会出现问题。
  • 是的,所以这不是问题所在。我在 Android 9 下运行该应用程序没有任何问题,以及使用 Android 4 到 9 的 25k 用户。只有两个用户报告了这个错误。设备上不能有一个位置保存某种缓存吗?
  • 我不知道。 OTOH,某些设备制造商可能做了一些奇怪的事情。两台有问题的设备的型号和操作系统版本是什么?

标签: android https ssl-certificate renewal


【解决方案1】:

我终于找到了解决办法,感谢Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error

首先下载问题网站的HTTPS证书(我用火狐做的),放到你的assets文件夹下。然后扩展Application,并添加以下内容:

public class MyApplication extends Application {

    private static SSLSocketFactory _sslSocketFactory = null;

    @Override
    public void onCreate() {

        super.onCreate();

        installSslIfNeeded();
        loadSslSocketFactoryIfNeeded();
    }

    @Nullable
    public static SSLSocketFactory getSslSocketFactory() {
        return _sslSocketFactory;
    }

    private void installSslIfNeeded() {

        // Install SSL certificates if needed:
        // See: https://stackoverflow.com/questions/29916962/javax-net-ssl-sslhandshakeexception-javax-net-ssl-sslprotocolexception-ssl-han
        try {
            ProviderInstaller.installIfNeeded(this);
            SSLContext sslContext;
            sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, null, null);
            sslContext.createSSLEngine();
        }
        catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException | NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); }

    }

    private void loadSslSocketFactoryIfNeeded() {

        // Create a static SSL factory trusting the server's HTTPS certificate whose authority
        // is unknown for Android < 5
        // https://developer.android.com/training/articles/security-ssl

        if (_sslSocketFactory == null) {

            try {

                // Load certificate:
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                InputStream caInput = getAssets().open("theserver.crt");
                Certificate ca;
                //noinspection TryFinallyCanBeTryWithResources
                try { ca = cf.generateCertificate(caInput); }
                finally { caInput.close(); }

                // Create a KeyStore containing our trusted CAs
                KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                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 an SSLContext that uses our TrustManager
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, tmf.getTrustManagers(), null);

                _sslSocketFactory = sslContext.getSocketFactory();
            }
            catch (CertificateException e) { e.printStackTrace(); }
            catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
            catch (KeyStoreException e) { e.printStackTrace(); }
            catch (IOException e) { e.printStackTrace(); }
            catch (KeyManagementException e) { e.printStackTrace(); }
        }
    }
}

现在,如果您想从 REST API 下载 JSON 文件,您可以这样做:

static JSONObject readJsonFromUrl(String urlString) throws IOException, JSONException {

    // Tell the URLConnection to use a SocketFactory from our SSLContext
    URL url = new URL(urlString);
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(MyApplication.getSslSocketFactory());

    return readJsonFromInputStream(urlConnection.getInputStream());
}

如果您使用的是 webview,则需要执行以下操作:

_webview.setWebViewClient(new WebViewClient() {

    [...]

    @Override
    public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {

        final AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
        builder.setMessage(R.string.my_ssl_error_message);

        builder.setPositiveButton(R.string.common_continue, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                handler.proceed();
            }
        });

        builder.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                handler.cancel();
            }
        });

        builder.show();
    }
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-15
    • 2011-06-05
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    相关资源
    最近更新 更多