Writing a SSL Checker using Java

11,120

Solution 1

To get the server certificate in the first place in order to do the verification manually, irrespectively of whether it's valid or not, the easiest is to connect via an SSLSocket after having disabled any certificate verification.

Create an SSLContext that lets anything through:

SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager passthroughTrustManager = new X509TrustManager() {
    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
};
sslContext.init(null, new TrustManager[] { passthroughTrustManager },
        null);

(Side note: for people looking for a way to use test certificates internally, I'd recommend building your own test CA rather than disabling checks, just in case those dummy checks are left over in production code by mistake, and also because it makes the tests more realistic.)

Create a socket, connect and start the handshake explicitly (since you're not really going to read form it):

SSLSocketFactory ssf = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket) ssf.createSocket(
        "the.host.name", 443);
socket.startHandshake();

Get the peer certificates chain. The first item is the actual server certificate.

X509Certificate[] peerCertificates = (X509Certificate[]) socket
        .getSession().getPeerCertificates();

If you want to validate against the default trust anchors (the default trusted CA certificates), build a X509TrustManager based on the default values (this is effectively what the passthroughTrustManager above disabled):

// By default on Oracle JRE, algorithm is PKIX
TrustManagerFactory tmf = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
// 'null' will initialise the tmf with the default CA certs installed
// with the JRE.
tmf.init((KeyStore) null);
X509TrustManager tm = (X509TrustManager) tmf.getTrustManagers()[0];

Now, check whether the certificate is currently valid in time, whether it's verified against a trusted anchor and whether it has the right extensions according to RFC 3280.

try {
    // Assuming RSA key here.
    tm.checkServerTrusted(peerCertificates, "RSA");
} catch (CertificateException e) {
    // Here you may check which subclass of CertificateException to know what the error is.
}

All the above makes use of the JSSE API.

Alternatively, if you want to dig deeper into the details of PKIX, you can use the Java Certification Path API (which is used by JSSE).

(If you only want valid certificates anyway to start with, just use SSLContext sslContext = SSLContext.getDefault() at the start, create the socket and do the handshake.)

To get the server certificate directly, you can use this:

X509Certificate serverCert = peerCertificates[0];

X509Certificate has a number of methods regarding dates and various extensions. For example:

Date expirationDate = serverCert.getNotAfter();

The hostname verification should follow RFC 6125 (or at least RFC 2818). In short, check whether the hostname you intended to connect to is one of the Subject Alternative Names (SAN) DNS entry. Failing that, fall back on checking whether the hostname is in the CN RDN of your certificate (for this, you need to split the Subject DN into RDNs). You may also be interested in this discussion (on the specific case of using IP addresses).

It all depends on how much "manual" verification you want to make. There are a number of specifications to read (RFC 5280/RFC 3280 and RFC 6125/RFC 2818 at least), in addition to the API documentations.

This question on Security.SE should also be of interest:

Solution 2

What you can do is implement your own checker on top of e.g Apache's HttpClient
You should initialize an SSL context and override the TrustManagers to do any checking you want.

The hostname verification can be done automatically by the library.

E.g. the following will configure the SSL socket handling to throw an exception if the hostname does not match the certificate's info:

SSLSocketFactory sf = new SSLSocketFactory(
    SSLContext.getInstance("TLS"),
    SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
Share:
11,120
W3B5T4R
Author by

W3B5T4R

Updated on June 04, 2022

Comments

  • W3B5T4R
    W3B5T4R almost 2 years

    Does anyone know of any good tutorials, sites, and or books on writing a SSL checker in Java? I'm trying to do what can be found here: http://www.sslshopper.com/ssl-checker.html . I'm not trying to create a self signed cert or use a a keystore. I want to be able to go out to any site determine if a valid SSL Certificate exists, determine if the hostname on the Cert matches the named entered, and determine when this Cert will expire. I have googled this topic but "How to create a SSL shopper using Java" hasn't yielded me anything and my other searches only brought me links on how to create a self-signed Cert. Any help would be greatly appreciated.

  • W3B5T4R
    W3B5T4R over 12 years
    I get nonconvertible types when trying to do this: X509Certificate[] peerCertificates = (X509Certificate[])socket.getSession().getPeerCertificates()‌​;
  • W3B5T4R
    W3B5T4R over 12 years
    I even tried an example from Oracles's site using the above and keep getting the "inconvertible types"
  • W3B5T4R
    W3B5T4R over 12 years
    Never mind I see, I was using import javax.security.cert.X509Certificate; instead of import java.security.cert.X509Certificate;