简体   繁体   English

HTTPS 证书更新后无法从 Android 应用访问网站

[英]Can't access website from Android app after HTTPS certificate renewing

I have an Android app using web services from an external server (that I do not control).我有一个使用来自外部服务器(我无法控制)的 Web 服务的 Android 应用程序。 Recently that server failed to renew its HTTPS certificate, and was unavailable for a few hours.最近该服务器未能更新其 HTTPS 证书,并且有几个小时不可用。 During this time interval, a few users of my app attempted to use the services, which naturally failed.在这段时间里,我的应用程序的一些用户尝试使用这些服务,这自然失败了。 The problem is that now that the problem is fixed on the server, these users are still unable to access the website from my app.问题是现在服务器上解决了问题,这些用户仍然无法从我的应用程序访问该网站。 One user can't even access the website from his mobile device's browser, another one is only blocked when trying from my app.一位用户甚至无法从其移动设备的浏览器访问该网站,另一位用户仅在使用我的应用程序尝试时才被阻止。

I have limited experience with HTTPS certificates renewing, so I'd like to know what could be wrong?我在 HTTPS 证书更新方面的经验有限,所以我想知道可能出了什么问题? It seems like these devices have kept in cache the expired certificate, and do not take the new one.似乎这些设备将过期的证书保存在缓存中,而不是使用新的证书。 Reinstalling my app doesn't fix the problem.重新安装我的应用程序并不能解决问题。

Thanks谢谢

I finally found a solution, thanks to Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, usually a protocol error .我终于找到了解决方案,感谢Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: Failure in SSL library, 通常是协议错误

First download the HTTPS certificate of the problematic website (I used Firefox to do it) and put it in your assets folder.首先下载有问题的网站的HTTPS证书(我是用Firefox做的),放到你的assets文件夹中。 Then extend Application, and add the following:然后扩展应用程序,并添加以下内容:

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(); }
        }
    }
}

Now, if you want for instance to download a JSON file from a REST API, you can do it this way:现在,如果你想从 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());
}

If you're using a webview, you need to do the following:如果您使用的是 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();
    }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 digicert 在服务器中更新证书后 Android ssl 证书错误 - Android ssl certificate error after renewing the certificate in server using digicert 无法将网站上的HTTPS内容调用到android应用 - Can't call HTTPS content on a website to android app 无法从我的Android模拟器访问HTTPS - Can't access HTTPS from my Android Emulator 如何从应用程序内部访问android应用程序的证书? - how to access android app's certificate from inside the app? 我是否可以从我的应用程序访问Android中的可信证书条目 - Can I have access to Trusted Certificate Entries in Android from my App 与Android应用程序中的客户端证书的HTTPS连接 - HTTPS connection with client certificate in an android app 在Openshift上发布网站后,无法在Android应用上获取FCM有效负载消息 - Can't get FCM payload message on Android app after website publish on Openshift 解析本地数据存储区:强制关闭应用程序后,无法从网络访问ACL保护的对象吗? (机器人) - Parse Local Datastore: I can't access ACL secured objects from the network after force closing the app? (Android) Android:无法通过HTTPS访问本地网站 - Android: Unable to access a local website over HTTPS 为什么我的应用程序无法从 Android 应用程序中链接到我的网站 - Why can't my app link to my website from within the Android app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM