To obtain a server certificate in the first place, to perform a manual check, regardless of whether it is valid or not, the easiest way to connect is through SSLSocket after disabling certificate verification.
Create an SSLContext that allows anything:
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);
(Lateral note: for people who are looking for a way to use test certificates domestically, I would recommend creating your own test CA instead of disabling checks, just in case these dummy checks are mistakenly left in the production code, and also because it makes tests more realistic.)
Create a socket, connect and start the handshake explicitly (since you are not really going to read its form):
SSLSocketFactory ssf = sslContext.getSocketFactory(); SSLSocket socket = (SSLSocket) ssf.createSocket( "the.host.name", 443); socket.startHandshake();
Get a peer certificate chain. The first element is the actual server certificate.
X509Certificate[] peerCertificates = (X509Certificate[]) socket .getSession().getPeerCertificates();
If you want to confirm that there are default trusted certificates (default trusted CA certificates), build the X509TrustManager based on the default values (this is actually what passthroughTrustManager 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 if the certificate is valid over time, whether it is verified by a trusted anchor and whether it has the correct extensions in accordance with 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 of the above uses the JSSE API .
Alternatively, if you want to delve into the PKIX details, you can use the Java Certification Paths API (which is used by JSSE).
(If you want to use only valid certificates, just use SSLContext sslContext = SSLContext.getDefault() at the beginning, create a socket and make a 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 instance:
Date expirationDate = serverCert.getNotAfter();
Hostname validation should follow RFC 6125 (or at least RFC 2818 ). In short, check to see if the host name you are going to connect to is one of the Subject Alternative Names (SAN) DNS records. Otherwise, return by checking if the host name is in the CN RDN of your certificate (for this you need to split the object DN by RDN). You may also be interested in this discussion (in the specific case of using IP addresses) .
It all depends on how many "manual" checks you want to do. There are a number of read specifications (RFC 5280 / RFC 3280 and RFC 6125 / RFC 2818, at least), in addition to the API docs.
This Security.SE question should also be of interest: