如何检查PostgreSQL服务器的SSL证书?


14

假设有一个正在运行的PostgreSQL服务器并且已启用SSL。使用“标准” Linux和PostgreSQL工具,如何检查其SSL证书?

我希望输出的结果类似于您从运行中得到的结果openssl x509 -text ...。而且我希望得到一两个命令行的答案,因此我不必求助于运行数据包嗅探器。

我无权访问PostgreSQL服务器,因此无法直接查看其配置文件。

我没有超级用户登录名,因此无法获取ssl_cert_file设置的值,然后pg_read_file就不能对其进行设置。

使用openssl s_client -connect ...不起作用,因为PostgreSQL似乎不想立即进行SSL握手。

通过快速浏览psql文档,我找不到命令行参数,该参数使它在启动时显示该信息。(尽管它确实向我显示了某些密码信息。)

Answers:


7

看起来OpenSSL的s_client工具使用-starttls1.1.1 添加了Postgres支持,因此您现在可以使用OpenSSL的命令行工具的全部功能,而无需其他帮助脚本:

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

参考文献:


10

遵循Craig Ringer评论中的想法:

一种选择是openssl s_client使用PostgreSQL协议修补握手。您也可以通过将自定义SSLSocketFactory传递给PgJDBC来使用Java。我不确定是否有任何简单的选择。

...我写了一个简单的SSL套接字工厂。我复制了PgJDBC自己的NonValidatingFactory类的代码,只是添加了代码以打印证书。

全部说完之后,看起来是这样的:

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());
            }
        }
    }
}

你摇滚。刚刚将其添加到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

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。尝试命令“哪个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());
            }
        }
    }
}

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]));
            }
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.