Как проверить SSL-сертификат сервера PostgreSQL?

14

Предположим, что работает сервер PostgreSQL и на нем включен SSL. Используя «стандартные» инструменты Linux и PostgreSQL, как я могу проверить его сертификат SSL?

Я надеюсь на результат, похожий на тот, который вы получите от бега openssl x509 -text .... И я надеюсь получить ответ из командной строки с одним или двумя строками, чтобы мне не пришлось прибегать к анализатору пакетов.

У меня нет доступа к серверу PostgreSQL, поэтому я не могу просмотреть его файлы конфигурации напрямую.

У меня нет логина суперпользователя, поэтому я не могу получить значение ssl_cert_fileпараметра и затем pg_read_fileего.

Использование openssl s_client -connect ...не работает, потому что PostgreSQL, похоже, не хочет делать рукопожатие SSL сразу.

Из быстрого просмотра psqlдокументации я не смог найти параметр командной строки, который заставляет его показывать эту информацию при запуске. (Хотя это действительно показывает мне определенную информацию о шифре.)

CSD
источник

Ответы:

7

Похоже, что s_clientинструмент OpenSSL добавил поддержку Postgres с использованием версии -starttls1.1.1, так что теперь вы можете использовать всю мощь инструментов командной строки OpenSSL без дополнительных вспомогательных скриптов:

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

Ссылки:

Адам Баткин
источник
10

Следуя идее в комментарии Крейга Рингера:

Один из вариантов - установить патч openssl s_clientдля рукопожатия с использованием протокола PostgreSQL. Вы также можете сделать это с помощью Java, передав пользовательский SSLSocketFactory в PgJDBC. Я не уверен, что есть какие-либо простые варианты.

... Я написал простую фабрику сокетов SSL. Я скопировал код собственного NonValidatingFactoryкласса PgJDBC и просто добавил код для печати сертификатов.

Вот как это выглядело, когда все было сказано и сделано:

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());
            }
        }
    }
}
CSD
источник
ты жжешь. Только что добавил это в install-cert github.com/spyhunter99/installcert
шпион
Огромное спасибо. Для людей, не желающих использовать PGSimpleDataSource. Вот вариант использования обычной настройки драйвера JDBC: String connectionURL = "jdbc:postgresql://server:62013/dbname"; Properties props = new Properties(); props.setProperty("user", "username"); props.setProperty("password", "password"); props.setProperty("ssl", "true"); props.setProperty("sslfactory", DumperFactory.class.getName()); Connection con = null; // Load the Driver class. Class.forName("org.postgresql.Driver"); con = DriverManager.getConnection(connectionURL, props);
Markus
7

Если вы не хотите беспокоиться об установке Java и компиляции, и у вас уже есть Python, вы можете попробовать этот скрипт Python: https://github.com/thusoy/postgres-mitm/blob/master/postgres_get_server_cert.py

Я использую это, чтобы проверить даты сертификата:

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

Или для полного сертификата в виде текста:

postgres_get_server_cert.py example.com:5432 | openssl x509 -noout -text
mivk
источник
1
Чтобы использовать его без установки: curl https://raw.githubusercontent.com/thusoy/postgres-mitm/master/postgres_get_server_cert.py | python - example.com:5432(но будьте уверены в том, что вы выполняете таким образом !!)
Yajo
3

Ответ CSD действительно спас меня. Вот более подробное прохождение для тех из нас, кто не знает или забыл Java.

  1. Убедитесь, что ваш сервер может компилировать Java. Попробуйте выполнить команду «which javac», если она выводит что-то вроде «... no javac in ...», то вам нужно установить JDK (JRE не будет работать, у него есть «java», но не «javac»).

  2. Установите postgresql-jdbc, если у вас его еще нет. Для RHEL6 команда "yum install postgresql-jdbc". Выясните, где установлены файлы JAR. Их будет несколько, по одному для каждой версии. Я использовал "/usr/share/java/postgresql-jdbc3.jar".

  3. Скопируйте код csd и вставьте информацию базы данных (другой ответ) или используйте мою слегка измененную версию в конце этого ответа. Сохраните его в файле с именем «ShowPostgreSQLCert.java». Прописные / строчные буквы имеют значение, называйте это как угодно, и оно не будет компилироваться.

  4. В каталоге с файлом ShowPostgreSQLCert.java выполните следующую команду (при необходимости измените местоположение postgresql-jdbc3.jar): «javac -cp /usr/share/java/postgresql-jdbc3.jar ShowPostgreSQLCert.java». Теперь у вас должно быть 3 файла .class в одном каталоге.

  5. Наконец, выполните следующую команду: «java -cp.: / Usr / share / java / postgresql-jdbc3.jar ShowPostgreSQLCert». "." после "-cp" означает, что он должен искать в текущем каталоге файлы .class. Вы можете вставить полный путь к файлам классов здесь, просто не забудьте оставить «:» между путем и расположением файла .jar.

  6. Если вам нужно выполнить команду на другом компьютере, вам нужно установить один и тот же файл jar (postgresql-jdbc3.jar), или вы можете просто скопировать его с сервера, на котором вы скомпилировали файлы .class. Затем просто скопируйте файлы .class и запустите команду из 5. после изменения путей.

Я немного изменил код, чтобы вы могли передавать информацию о базе данных в командной строке, а не компилировать ее в файл .class. Просто запустите его без каких-либо аргументов, и он покажет сообщение, показывающее, какие аргументы он ожидает. код + модификации csd:

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();
        if( args.length != 4 ) {
            System.out.println("Not enough arguments. Usage: ShowPostgreSQLCert ServerName User DatabaseName Password");
            System.exit(1);
        }
        ds.setServerName( args[0] );
        ds.setSsl(true);
        ds.setUser( args[1] );
        ds.setDatabaseName( args[2] );
        ds.setPassword( args[3] );
        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());
            }
        }
    }
}
vidarsk
источник
1

Я добавил код из /programming/3313020/write-x509-certificate-into-pem-formatted-string-in-java для вывода сертификатов в виде PEM и удалил необходимость указывать БД, имя пользователя или пароль (они не нужны для получения сертификата).

Используя это, я смог убедиться, что перезапуск PostgreSQL, к сожалению, кажется необходимым для перехода на новый сертификат.

Не будучи Java-разработчиком, мои шаги по сборке и запуску, вероятно, не так хороши, но они работают, если вы можете найти postgresql jdbc

# locate postgresql | grep jar
/path/to/a/lib/postgresql-9.1-901-1.jdbc4.jar   <-- this one will do
...

Компилировать:

javac -cp /path/to/a/lib/postgresql-9.1-901-1.jdbc4.jar ./ShowPostgreSQLCert.java

Бежать:

java -cp /path/to/a/lib/postgresql-9.1-901-1.jdbc4.jar:. ShowPostgreSQLCert 127.0.0.1

Пример вывода:

Cert 1:
    Subject: CN=...
    Issuer: CN=...
    Not Before: Fri Oct 21 11:14:06 NZDT 2016
    Not After: Sun Oct 21 11:24:00 NZDT 2018
-----BEGIN CERTIFICATE-----
MIIHEjCCBfqgAwIBAgIUUbiRZjruNAEo2j1QPqBh6GzcNrwwDQYJKoZIhvcNAQEL
...
IcIXcVQxPzVrpIDT5G6jArVt+ERLEWs2V09iMwY7//CQb0ivpVg=
-----END CERTIFICATE-----

Cert 2:
...

Источник:

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;

import javax.xml.bind.DatatypeConverter;
import java.security.cert.X509Certificate;
import java.io.StringWriter;

public class ShowPostgreSQLCert {
    public static void main(String[] args) throws Throwable {
        PGSimpleDataSource ds = new PGSimpleDataSource();
        if( args.length != 1 ) {
            System.out.println("Not enough arguments.");
            System.out.println("Usage: ShowPostgreSQLCert ServerName");
            System.exit(1);
        }
        ds.setServerName( args[0] );
        ds.setSsl(true);
        ds.setUser( "" );
        ds.setDatabaseName( "" );
        ds.setPassword( "" );
        ds.setSslfactory(DumperFactory.class.getName());
        try (Connection c = ds.getConnection()) { }
        catch (org.postgresql.util.PSQLException e) {
            // Don't actually want to login
        }
    }

    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 String certToString(X509Certificate cert) {
        StringWriter sw = new StringWriter();
        try {
            sw.write("-----BEGIN CERTIFICATE-----\n");
            sw.write(DatatypeConverter.printBase64Binary(cert.getEncoded()).replaceAll("(.{64})", "$1\n"));
            sw.write("\n-----END CERTIFICATE-----\n");
        } catch (java.security.cert.CertificateEncodingException e) {
            e.printStackTrace();
        }
        return sw.toString();
    }

    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());
                System.out.println("    Not Before: " + certs[i].getNotBefore().toString());
                System.out.println("    Not After: " + certs[i].getNotAfter().toString());

                System.out.println(certToString(certs[i]));
            }
        }
    }
}
Кэмерон Керр
источник