CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-postgresql--postgresql

PostgreSQL JDBC Driver allows Java programs to connect to a PostgreSQL database using standard, database independent Java code.

Overview
Eval results
Files

ssl-security.mddocs/

SSL/TLS Security

This document covers SSL/TLS configuration for secure database connections, including SSL modes, certificate management, and custom SSL factories.

Capabilities

SSL Modes

PostgreSQL JDBC driver supports multiple SSL security levels.

package org.postgresql.jdbc;

/**
 * SSL connection modes controlling security level.
 */
public enum SslMode {
    /**
     * Do not use SSL encryption.
     * Connection is unencrypted.
     */
    DISABLE,

    /**
     * Try non-SSL connection first.
     * If server requires SSL, upgrade to SSL connection.
     * Not recommended for security.
     */
    ALLOW,

    /**
     * Try SSL connection first (default).
     * If server doesn't support SSL, fallback to non-SSL.
     * Provides encryption if available.
     */
    PREFER,

    /**
     * Require SSL connection.
     * Fail if server doesn't support SSL.
     * Does NOT verify server certificate.
     */
    REQUIRE,

    /**
     * Require SSL with server certificate verification.
     * Verifies certificate is signed by trusted CA.
     * Does NOT verify hostname.
     */
    VERIFY_CA,

    /**
     * Require SSL with full certificate verification.
     * Verifies certificate AND hostname match.
     * Most secure mode.
     */
    VERIFY_FULL;

    /**
     * Returns whether this mode requires an encrypted connection.
     *
     * @return true if REQUIRE or higher (VERIFY_CA, VERIFY_FULL)
     */
    public boolean requireEncryption();

    /**
     * Returns whether this mode verifies the server certificate.
     *
     * @return true for VERIFY_CA or VERIFY_FULL
     */
    public boolean verifyCertificate();

    /**
     * Returns whether this mode verifies the server hostname matches the certificate.
     *
     * @return true only for VERIFY_FULL
     */
    public boolean verifyPeerName();

    /**
     * Parses SSL mode from connection properties.
     * Falls back to checking the 'ssl' property if 'sslmode' is not set.
     *
     * @param info Connection properties
     * @return SslMode enum constant
     * @throws PSQLException if sslmode value is invalid
     */
    public static SslMode of(Properties info) throws PSQLException;
}

GSSAPI Encryption Modes

PostgreSQL JDBC driver supports GSSAPI encryption (Kerberos-based encryption) as an alternative to SSL.

package org.postgresql.jdbc;

/**
 * GSSAPI encryption modes for Kerberos-based secure connections.
 * Provides an alternative to SSL for environments using Kerberos authentication.
 */
public enum GSSEncMode {
    /**
     * Do not use GSSAPI encrypted connections.
     */
    DISABLE,

    /**
     * Start with non-encrypted connection, then try encrypted one.
     */
    ALLOW,

    /**
     * Start with encrypted connection, fallback to non-encrypted (default).
     */
    PREFER,

    /**
     * Ensure connection is encrypted via GSSAPI.
     */
    REQUIRE;

    /**
     * Returns whether this mode requires GSSAPI encryption.
     *
     * @return true if REQUIRE mode is set
     */
    public boolean requireEncryption();

    /**
     * Parses GSSAPI encryption mode from connection properties.
     * Falls back to ALLOW if gssEncMode is not set.
     *
     * @param info Connection properties
     * @return GSSEncMode enum constant
     * @throws PSQLException if gssEncMode value is invalid
     */
    public static GSSEncMode of(Properties info) throws PSQLException;
}

Configuration Examples:

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

// Example 1: SSL via URL
public class SSLURLExample {
    public static Connection connectSSL() throws SQLException {
        String url = "jdbc:postgresql://localhost:5432/mydb?ssl=true&sslmode=require";
        return DriverManager.getConnection(url, "user", "password");
    }
}

// Example 2: SSL via Properties
public class SSLPropertiesExample {
    public static Connection connectSSL() throws SQLException {
        String url = "jdbc:postgresql://localhost:5432/mydb";
        Properties props = new Properties();
        props.setProperty("user", "postgres");
        props.setProperty("password", "secret");
        props.setProperty("ssl", "true");
        props.setProperty("sslmode", "verify-full");
        return DriverManager.getConnection(url, props);
    }
}

// Example 3: SSL via DataSource
public class SSLDataSourceExample {
    public static Connection connectSSL() throws SQLException {
        PGSimpleDataSource ds = new PGSimpleDataSource();
        ds.setServerName("db.example.com");
        ds.setDatabaseName("mydb");
        ds.setUser("postgres");
        ds.setPassword("secret");
        ds.setSsl(true);
        ds.setSslMode("verify-full");
        return ds.getConnection();
    }
}

SSL Factories

Custom SSL socket factories for certificate management.

package org.postgresql.ssl;

import javax.net.ssl.SSLSocketFactory;

/**
 * Default SSL factory compatible with libpq certificate file locations.
 * Looks for certificates in ~/.postgresql/ directory:
 * - root.crt: trusted CA certificates
 * - postgresql.crt: client certificate
 * - postgresql.pk8: client private key (PKCS#8 format)
 */
public class LibPQFactory implements SSLSocketFactory {
    // Automatically loads certificates from standard locations
}

/**
 * SSL factory that does NOT validate server certificates.
 * WARNING: Vulnerable to man-in-the-middle attacks.
 * Only use for testing or when security is not required.
 */
public class NonValidatingFactory extends WrappedFactory {
    /**
     * Constructor accepting an optional argument.
     * The argument is ignored but presence avoids reflection lookup overhead.
     *
     * @param arg Optional argument (ignored)
     * @throws GeneralSecurityException if SSL context cannot be created
     */
    public NonValidatingFactory(String arg) throws GeneralSecurityException;
}

/**
 * SSL factory that validates against a single trusted certificate.
 * More secure than NonValidatingFactory as it pins the server certificate.
 * The certificate can be loaded from classpath, filesystem, environment variable, or inline.
 *
 * Configuration via sslfactoryarg connection property:
 * - classpath:ssl/server.crt - Load from classpath
 * - file:/path/to/server.crt - Load from filesystem
 * - env:MYDB_CERT - Load from environment variable
 * - sys:mydb.cert - Load from system property
 * - No prefix - Inline PEM-encoded certificate
 */
public class SingleCertValidatingFactory extends WrappedFactory {
    /**
     * Constructor accepting certificate specification via sslfactoryarg.
     *
     * @param arg Certificate source specification (see class documentation)
     * @throws GeneralSecurityException if certificate cannot be loaded or validated
     */
    public SingleCertValidatingFactory(String arg) throws GeneralSecurityException;
}

/**
 * SSL factory using Java's default SSL configuration.
 * Uses system truststore (cacerts) and keystore.
 * Always validates server certificate using Java's default truststore.
 */
public class DefaultJavaSSLFactory extends WrappedFactory {
    /**
     * Constructor using Java's default SSL socket factory.
     *
     * @param info Connection properties (unused)
     */
    public DefaultJavaSSLFactory(Properties info);
}

/**
 * Abstract base class for SSL factories using JKS keystore.
 * Subclasses must implement getKeyStorePassword() and getKeyStoreStream().
 * The keystore is used for both client certificates and trusted CAs.
 */
public abstract class DbKeyStoreSocketFactory extends WrappedFactory {
    /**
     * Constructor that loads and initializes the keystore.
     *
     * @throws DbKeyStoreSocketException if keystore cannot be loaded
     */
    public DbKeyStoreSocketFactory() throws DbKeyStoreSocketException;

    /**
     * Returns the password for the keystore.
     * Must be implemented by subclasses.
     *
     * @return Keystore password as char array
     */
    public abstract char[] getKeyStorePassword();

    /**
     * Returns an InputStream for reading the keystore.
     * Must be implemented by subclasses.
     *
     * @return InputStream for JKS keystore
     */
    public abstract InputStream getKeyStoreStream();

    /**
     * Exception thrown when keystore operations fail.
     */
    public static class DbKeyStoreSocketException extends Exception {
        public DbKeyStoreSocketException(String message);
    }
}

/**
 * Custom hostname verifier for SSL connections.
 */
public class PGjdbcHostnameVerifier implements javax.net.ssl.HostnameVerifier {
    @Override
    public boolean verify(String hostname, javax.net.ssl.SSLSession session);
}

Configuration Examples:

// Example 1: Using LibPQFactory (default)
public class LibPQFactoryExample {
    public static Connection connectWithLibPQ() throws SQLException {
        // Place certificates in ~/.postgresql/:
        // - root.crt (trusted CA certificates)
        // - postgresql.crt (client certificate)
        // - postgresql.pk8 (client private key)

        Properties props = new Properties();
        props.setProperty("user", "postgres");
        props.setProperty("password", "secret");
        props.setProperty("ssl", "true");
        props.setProperty("sslmode", "verify-full");
        props.setProperty("sslfactory", "org.postgresql.ssl.LibPQFactory");

        String url = "jdbc:postgresql://localhost/mydb";
        return DriverManager.getConnection(url, props);
    }
}

// Example 2: Using DefaultJavaSSLFactory
public class JavaSSLFactoryExample {
    public static Connection connectWithJavaSSL() throws SQLException {
        // Set JVM properties for truststore/keystore
        System.setProperty("javax.net.ssl.trustStore", "/path/to/truststore.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "password");
        System.setProperty("javax.net.ssl.keyStore", "/path/to/keystore.jks");
        System.setProperty("javax.net.ssl.keyStorePassword", "password");

        Properties props = new Properties();
        props.setProperty("user", "postgres");
        props.setProperty("password", "secret");
        props.setProperty("ssl", "true");
        props.setProperty("sslmode", "verify-full");
        props.setProperty("sslfactory", "org.postgresql.ssl.DefaultJavaSSLFactory");

        String url = "jdbc:postgresql://localhost/mydb";
        return DriverManager.getConnection(url, props);
    }
}

// Example 3: NonValidatingFactory (testing only)
public class NonValidatingExample {
    public static Connection connectWithoutValidation() throws SQLException {
        // WARNING: Do not use in production!
        Properties props = new Properties();
        props.setProperty("user", "postgres");
        props.setProperty("password", "secret");
        props.setProperty("ssl", "true");
        props.setProperty("sslfactory", "org.postgresql.ssl.NonValidatingFactory");

        String url = "jdbc:postgresql://localhost/mydb";
        return DriverManager.getConnection(url, props);
    }
}

Password Management

PostgreSQL JDBC driver provides a secure method to change user passwords.

package org.postgresql;

/**
 * Secure password change functionality (part of PGConnection interface).
 */
public interface PGConnection extends Connection {
    /**
     * Change a user's password to the specified new password.
     *
     * The password is encrypted locally before transmission, so the plain text
     * password is never sent on the wire. This provides secure password changes
     * even over non-SSL connections.
     *
     * If encryptionType is null, this method queries the database server for
     * the server's default password_encryption setting.
     *
     * The newPassword array is automatically zeroed out after use for security.
     *
     * @param user The username of the database user
     * @param newPassword The new password (will be zeroed after use)
     * @param encryptionType Encryption type: "md5", "scram-sha-256", or null for server default
     * @throws SQLException If the password could not be altered
     * @since 42.2.0
     */
    default void alterUserPassword(String user, char[] newPassword, String encryptionType)
            throws SQLException;
}

Usage Example:

import org.postgresql.PGConnection;
import java.sql.*;

public class PasswordManagementExample {
    public static void changePassword(Connection conn, String username) throws SQLException {
        // Unwrap to get PGConnection interface
        PGConnection pgConn = conn.unwrap(PGConnection.class);

        // New password as char array (more secure than String)
        char[] newPassword = "newSecurePassword123!".toCharArray();

        try {
            // Use SCRAM-SHA-256 for maximum security (PostgreSQL 10+)
            pgConn.alterUserPassword(username, newPassword, "scram-sha-256");
            System.out.println("Password changed successfully for user: " + username);
        } finally {
            // Password array is automatically zeroed by the method
            // No need to manually zero it
        }
    }

    public static void changePasswordWithServerDefault(Connection conn, String username)
            throws SQLException {
        PGConnection pgConn = conn.unwrap(PGConnection.class);

        char[] newPassword = "anotherSecurePassword!".toCharArray();

        // Use null to let server decide encryption type (recommended)
        pgConn.alterUserPassword(username, newPassword, null);
        System.out.println("Password changed using server default encryption");
    }
}

Important Notes:

  • Use null for encryptionType: Recommended to use the server's default
  • Avoid "md5": Only use for legacy servers that don't support SCRAM-SHA-256
  • Use "scram-sha-256": Most secure option for PostgreSQL 10+
  • char[] vs String: Password is passed as char[] for security (can be zeroed)
  • Automatic cleanup: The method automatically zeros the password array after use
  • Local encryption: Password is encrypted locally, never sent in plain text
  • Permissions required: User must have ALTER USER privilege or be superuser

Certificate Configuration

Connection Properties:

/**
 * SSL-related connection properties
 */
public class SSLProperties {
    /**
     * ssl - Enable SSL connection
     * Values: true, false
     * Default: false
     */
    String ssl;

    /**
     * sslmode - SSL security mode
     * Values: disable, allow, prefer, require, verify-ca, verify-full
     * Default: prefer
     */
    String sslmode;

    /**
     * sslfactory - SSL socket factory class
     * Default: org.postgresql.ssl.LibPQFactory
     */
    String sslfactory;

    /**
     * sslcert - Client certificate file path
     * Default: ~/.postgresql/postgresql.crt
     */
    String sslcert;

    /**
     * sslkey - Client private key file path
     * Default: ~/.postgresql/postgresql.pk8
     */
    String sslkey;

    /**
     * sslrootcert - Root certificate file path (trusted CAs)
     * Default: ~/.postgresql/root.crt
     */
    String sslrootcert;

    /**
     * sslpassword - Password for encrypted private key
     */
    String sslpassword;

    /**
     * sslpasswordcallback - Callback class for password
     */
    String sslpasswordcallback;

    /**
     * sslhostnameverifier - Custom hostname verifier class
     */
    String sslhostnameverifier;

    /**
     * sslfactoryarg - Argument passed to SSL factory
     */
    String sslfactoryarg;
}

Usage Examples:

// Example 1: Custom certificate locations
public class CustomCertificates {
    public static Connection connectWithCustomCerts() throws SQLException {
        Properties props = new Properties();
        props.setProperty("user", "postgres");
        props.setProperty("password", "secret");
        props.setProperty("ssl", "true");
        props.setProperty("sslmode", "verify-full");
        props.setProperty("sslcert", "/path/to/client.crt");
        props.setProperty("sslkey", "/path/to/client.pk8");
        props.setProperty("sslrootcert", "/path/to/ca.crt");

        String url = "jdbc:postgresql://db.example.com/mydb";
        return DriverManager.getConnection(url, props);
    }
}

// Example 2: Encrypted private key
public class EncryptedKey {
    public static Connection connectWithEncryptedKey() throws SQLException {
        Properties props = new Properties();
        props.setProperty("user", "postgres");
        props.setProperty("password", "secret");
        props.setProperty("ssl", "true");
        props.setProperty("sslmode", "verify-full");
        props.setProperty("sslkey", "/path/to/encrypted.pk8");
        props.setProperty("sslpassword", "key-password");

        String url = "jdbc:postgresql://localhost/mydb";
        return DriverManager.getConnection(url, props);
    }
}

Certificate Formats

Supported Formats:

  1. Client Certificate: X.509 PEM format (.crt, .pem)

    -----BEGIN CERTIFICATE-----
    ...certificate data...
    -----END CERTIFICATE-----
  2. Private Key: PKCS#8 unencrypted or encrypted format (.pk8, .key)

    -----BEGIN PRIVATE KEY-----
    ...key data...
    -----END PRIVATE KEY-----
  3. Root Certificate: X.509 PEM format with one or more CA certificates

    -----BEGIN CERTIFICATE-----
    ...CA cert 1...
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    ...CA cert 2...
    -----END CERTIFICATE-----

Converting Certificates:

# Convert PKCS#1 to PKCS#8 (required format)
openssl pkcs8 -topk8 -inform PEM -outform PEM -in postgresql.key -out postgresql.pk8 -nocrypt

# Convert PKCS#12 to PEM
openssl pkcs12 -in cert.p12 -out postgresql.crt -clcerts -nokeys
openssl pkcs12 -in cert.p12 -out postgresql.key -nocerts -nodes

# Extract CA certificate
openssl s_client -connect db.example.com:5432 -starttls postgres < /dev/null | openssl x509 > root.crt

Custom SSL Factory

Implementing a custom SSL factory:

import javax.net.ssl.*;
import java.security.KeyStore;
import java.io.FileInputStream;

public class CustomSSLFactory extends SSLSocketFactory {
    private SSLSocketFactory factory;

    public CustomSSLFactory() throws Exception {
        // Load truststore
        KeyStore trustStore = KeyStore.getInstance("JKS");
        try (FileInputStream fis = new FileInputStream("/path/to/truststore.jks")) {
            trustStore.load(fis, "password".toCharArray());
        }

        // Create TrustManager
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(trustStore);

        // Create SSLContext
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(null, tmf.getTrustManagers(), null);

        factory = ctx.getSocketFactory();
    }

    // Implement SSLSocketFactory methods delegating to factory
    @Override
    public Socket createSocket(String host, int port) throws IOException {
        return factory.createSocket(host, port);
    }

    // ... other methods
}

// Usage
Properties props = new Properties();
props.setProperty("ssl", "true");
props.setProperty("sslfactory", "com.example.CustomSSLFactory");

Security Best Practices

  1. Always use SSL in production

    props.setProperty("ssl", "true");
    props.setProperty("sslmode", "verify-full");
  2. Verify server certificates

    Use verify-full mode to prevent MITM attacks
  3. Protect private keys

    - Use file permissions (chmod 600)
    - Encrypt keys with passwords
    - Store in secure locations
  4. Use strong cipher suites

    // Disable weak ciphers via JVM properties
    System.setProperty("jdk.tls.disabledAlgorithms",
            "SSLv3, RC4, DES, MD5withRSA, DH keySize < 2048");
  5. Rotate certificates regularly

    Implement certificate expiry monitoring
  6. Use certificate pinning for critical systems

    // Implement custom SSLFactory with certificate pinning
  7. Monitor SSL configuration

    // Log SSL connection details
    Logger logger = Logger.getLogger("org.postgresql");
    logger.setLevel(Level.FINE);

Common SSL Configurations

Development (localhost):

ssl=false

Testing (self-signed certs):

ssl=true
sslmode=require
sslfactory=org.postgresql.ssl.NonValidatingFactory

Production (CA-signed certs):

ssl=true
sslmode=verify-full
sslrootcert=/path/to/ca-bundle.crt

Production (client certificates):

ssl=true
sslmode=verify-full
sslcert=/path/to/client.crt
sslkey=/path/to/client.pk8
sslrootcert=/path/to/ca-bundle.crt

Cloud Providers (AWS RDS, Azure, GCP):

ssl=true
sslmode=verify-full
# Download provider's CA certificate
sslrootcert=/path/to/provider-ca.crt

Install with Tessl CLI

npx tessl i tessl/maven-org-postgresql--postgresql

docs

advanced-features.md

basic-connectivity.md

copy-operations.md

datasource.md

index.md

large-objects.md

postgresql-types.md

replication.md

resultset.md

ssl-security.md

statement-execution.md

transactions.md

tile.json