Validate X.509 certificate against CA in Java

Viewed 36611

Lets say I have something like this (client side code):

TrustManager[] trustAllCerts = new TrustManager[]{
    new X509TrustManager() {

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
        }

        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
        }
    }
};

SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(null, trustAllCerts, null);

SocketFactory sf = sslc.getSocketFactory();
SSLSocket s = (SSLSocket) sf.createSocket("127.0.0.1", 9124);

This code is complete functional, but I really can not figure out, how to validate server's certificate against one concrete CA certificate that I have available in pem file.

All certificates are signed by my self-signed CA, and it is the CA I need to validate against (only against this one).

Every answer is appreciated.

EDIT:

In response to jglouie (thank you very much this way - can not vote up your answer).

I founded the solution:

new X509TrustManager() {

        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType) {
        }

        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
                throws CertificateException {
            InputStream inStream = null;
            try {
                // Loading the CA cert
                URL u = getClass().getResource("tcp/cacert.pem");
                inStream = new FileInputStream(u.getFile());
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                X509Certificate ca = (X509Certificate) cf.generateCertificate(inStream);
                inStream.close();

                for (X509Certificate cert : certs) {
                    // Verifing by public key
                    cert.verify(ca.getPublicKey());
                }
            } catch (Exception ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                try {
                    inStream.close();
                } catch (IOException ex) {
                    Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }
    }
};
3 Answers

The accepted answer is extremely incorrect. It doesn't cryptographically verify any connection between the server certificate and the trusted certificate authority. In general, you should almost never need to implement your own TrustManager, doing so is extremely dangerous.

As EJP stated, there's no need to implement your own TrustManager, you can just use the default one, and ensure that the trusted CA certificate has been added to your default TrustStore. See this question for more information.

Take a look at the CertPathValidator class from the JDK, which verifies a continuous chain of trust from the server's own certificate up through a trusted CA. See Oracle's docs for an introduction to certificate chain validation.

Related