How to ignore expired certificates from outside a

2020-04-29 16:27发布

问题:

We have a Java application that we need to ignore an expired self-signed cert, however we cannot modify the code to do this. I wondering if there was a System Property or environment variable that we could provide at start up that would allow us to have all expired cert's ignored for now, or even be more specific and provide externally the specific cert that we would like to have the expiration ignored.

Anyone have any ideas that would work?

回答1:

Here is the solution obtained by combining java - ignore expired ssl certificate and Java SSL: how to disable hostname verification.

public class IgnoreExpiredServerCertificateAgent {

    public static void premain(String args, Instrumentation inst) throws Exception {
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init((KeyStore) null);

        TrustManager[] trustManagers = tmf.getTrustManagers();
        final X509TrustManager origTrustManager = (X509TrustManager) trustManagers[0];

        TrustManager[] wrappedTrustManagers = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return origTrustManager.getAcceptedIssuers();
                    }

                    @Override
                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        origTrustManager.checkClientTrusted(certs, authType);
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        try {
                            origTrustManager.checkServerTrusted(certs, authType);
                        } catch (CertificateExpiredException ignored) {
                        }
                    }
                }
        };

        //SSLContext sc = SSLContext.getDefault();
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, wrappedTrustManagers, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }
}

Then just add -javaagent:IgnoreExpiredServerCertificateAgent.jar to program's java startup arguments.

See also Difference between SSL and TLS and their usage in Java and Java 8 SSLContext.getInstance("TLSv1.2") what does it mean? for appropriate argument for SSLContext.getInstance() in your case.


Also note that the server with expired certificate may also itself check the expiry of the matching client certificate:

Caused by:javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_expired at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174) at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1822) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1004) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1188) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1215) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1199) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1195) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:379) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:318)

If you meet such stacktrace then there is no way to mitigate the problem without touching the server. And the proper solution would be to reissue the certificate with future expiry date.