Search code examples
androidandroid-securityx509trustmanager

Does X509TrustManager check API requests only one time per Android Application session?


Recently I have implemented a security feature to check my request is connected with the valid host. For that, I'm checking the certs of that host and I used X509TrustManager for that case. So if X509TrustManager found some invalid cert it will throw an exception and according to that, I will show an alert to the user. But the issue is that X509TrustManager throws an exception only first time. But when I refresh the same request I did not catch invalid certification and I did not see any alert. Below I added my implementation. Let me know any issue with my implementation or any known issue with X509TrustManager. Thanks and Regards.

final X509TrustManager finalTrustManager = x509TrustManager;
    TrustManager[] trustAllCerts = new TrustManager[0];
    if (finalTrustManager != null) {
        trustAllCerts = new TrustManager[]{
                new X509TrustManager() {

                    public X509Certificate[] getAcceptedIssuers() {
                        return finalTrustManager.getAcceptedIssuers();
                    }

                    @Override
                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        try {
                            // If Application get any CertificateException in Splash screen we will show related alert in MainActivity
                            // We need to terminate app after showing alert but if we show alert in Splash screen it will get hide when Main Activity get visible.
                            // To avoid this scenario we added this implementation.
                            if (mIsSplashGetInvalidateCertificate && !(mLifecycleManager.getCurrentStackOfActivity().get(0) instanceof SplashActivity)) {
                                mAlertManager.showAlertMessageWithoutDuplicates(mLifecycleManager.getCurrentContext().getResources().getString(R.string.certificate_error_title), mLifecycleManager.getCurrentContext().getResources().getString(R.string.certificate_error_message), (FragmentActivity) mLifecycleManager.getCurrentStackOfActivity().get(0), true);
                            }

                            // Checking the certificate availability of host
                            if ((certs != null && certs.length != 0) && (authType != null && authType.length() != 0)) {
                                finalTrustManager.checkClientTrusted(certs, authType);
                            } else {
                                terminateApplicationWithAlert();
                            }
                        } catch (CertificateException e) {
                            terminateApplicationWithAlert();
                        }
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        try {
                            if (mIsSplashGetInvalidateCertificate && !(mLifecycleManager.getCurrentStackOfActivity().get(0) instanceof SplashActivity)) {
                                mAlertManager.showAlertMessageWithoutDuplicates(mLifecycleManager.getCurrentContext().getResources().getString(R.string.certificate_error_title), mLifecycleManager.getCurrentContext().getResources().getString(R.string.certificate_error_message), (FragmentActivity) mLifecycleManager.getCurrentStackOfActivity().get(0), true);
                            }

                            if ((certs != null && certs.length != 0) && (authType != null && authType.length() != 0)) {
                                finalTrustManager.checkServerTrusted(certs, authType);
                            } else {
                                terminateApplicationWithAlert();
                            }
                        } catch (CertificateException e) {
                            terminateApplicationWithAlert();
                        }
                    }
                }
        };
    }

Solution

  • You're not actually flagging the certificate as invalid, because you're catching the CertificateException and swallowing it. By not throwing CertificateException, you are telling the HTTP library that the invalid certificate is valid, which it's presumably caching in order to not revalidate the certificate too many times.

    You need to allow the CertificateException to be thrown from the X509TrustManager methods, catch the error at the HTTP call sites, and show the dialog from there.