How to examine PostgreSQL server's SSL certificate?

It looks like OpenSSL's s_client tool added Postgres support using the -starttls in 1.1.1, so you can now use the full power of OpenSSL's command line tools without additional helper scripts:

openssl s_client -starttls postgres -connect my.postgres.host:5432 # etc...

References:

  • Git commit
  • s_client manpage

Following the idea in Craig Ringer's comment:

One option is to patch openssl s_client to handshake with the PostgreSQL protocol. You can probably also do it with Java, by passing a custom SSLSocketFactory to PgJDBC. I'm not sure there are any simple options.

...I wrote a simple SSL socket factory. I copied the code of PgJDBC's own NonValidatingFactory class and just added code to print the certificates.

Here's what it looked like, when it was all said and done:

import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.sql.Connection;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.postgresql.ds.PGSimpleDataSource;
import org.postgresql.ssl.WrappedFactory;

public class ShowPostgreSQLCert {
    public static void main(String[] args) throws Throwable {
        PGSimpleDataSource ds = new PGSimpleDataSource();
        ds.setServerName( ... );
        ds.setSsl(true);
        ds.setUser( ... );
        ds.setDatabaseName( ... );
        ds.setPassword( ... );
        ds.setSslfactory(DumperFactory.class.getName());
        try (Connection c = ds.getConnection()) { }
    }

    public static class DumperFactory extends WrappedFactory {
        public DumperFactory(String arg) throws GeneralSecurityException {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] { new DumperTM() }, null);
            _factory = ctx.getSocketFactory();
        }
    }

    public static class DumperTM implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
        public void checkClientTrusted(X509Certificate[] certs, String authType) { }
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            for (int i=0; i<certs.length; ++i) {
                System.out.println("Cert " + (i+1) + ":");
                System.out.println("    Subject: " + certs[i].getSubjectX500Principal().getName());
                System.out.println("    Issuer: " + certs[i].getIssuerX500Principal().getName());
            }
        }
    }
}

If you don't want to bother with installing java and compiling, and you already have python, you could try this python script: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py

I use it to check certificate dates:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -dates

Or for the full cert as text:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -text