PostgreSQL JDBC Driver allows Java programs to connect to a PostgreSQL database using standard, database independent Java code.
This document covers SSL/TLS configuration for secure database connections, including SSL modes, certificate management, and custom SSL factories.
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;
}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();
}
}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);
}
}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:
null for encryptionType: Recommended to use the server's defaultConnection 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);
}
}Supported Formats:
Client Certificate: X.509 PEM format (.crt, .pem)
-----BEGIN CERTIFICATE-----
...certificate data...
-----END CERTIFICATE-----Private Key: PKCS#8 unencrypted or encrypted format (.pk8, .key)
-----BEGIN PRIVATE KEY-----
...key data...
-----END PRIVATE KEY-----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.crtImplementing 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");Always use SSL in production
props.setProperty("ssl", "true");
props.setProperty("sslmode", "verify-full");Verify server certificates
Use verify-full mode to prevent MITM attacksProtect private keys
- Use file permissions (chmod 600)
- Encrypt keys with passwords
- Store in secure locationsUse strong cipher suites
// Disable weak ciphers via JVM properties
System.setProperty("jdk.tls.disabledAlgorithms",
"SSLv3, RC4, DES, MD5withRSA, DH keySize < 2048");Rotate certificates regularly
Implement certificate expiry monitoringUse certificate pinning for critical systems
// Implement custom SSLFactory with certificate pinningMonitor SSL configuration
// Log SSL connection details
Logger logger = Logger.getLogger("org.postgresql");
logger.setLevel(Level.FINE);Development (localhost):
ssl=falseTesting (self-signed certs):
ssl=true
sslmode=require
sslfactory=org.postgresql.ssl.NonValidatingFactoryProduction (CA-signed certs):
ssl=true
sslmode=verify-full
sslrootcert=/path/to/ca-bundle.crtProduction (client certificates):
ssl=true
sslmode=verify-full
sslcert=/path/to/client.crt
sslkey=/path/to/client.pk8
sslrootcert=/path/to/ca-bundle.crtCloud Providers (AWS RDS, Azure, GCP):
ssl=true
sslmode=verify-full
# Download provider's CA certificate
sslrootcert=/path/to/provider-ca.crtInstall with Tessl CLI
npx tessl i tessl/maven-org-postgresql--postgresql