0
# SSL/TLS Security
1
2
This document covers SSL/TLS configuration for secure database connections, including SSL modes, certificate management, and custom SSL factories.
3
4
## Capabilities
5
6
### SSL Modes
7
8
PostgreSQL JDBC driver supports multiple SSL security levels.
9
10
```java { .api }
11
package org.postgresql.jdbc;
12
13
/**
14
* SSL connection modes controlling security level.
15
*/
16
public enum SslMode {
17
/**
18
* Do not use SSL encryption.
19
* Connection is unencrypted.
20
*/
21
DISABLE,
22
23
/**
24
* Try non-SSL connection first.
25
* If server requires SSL, upgrade to SSL connection.
26
* Not recommended for security.
27
*/
28
ALLOW,
29
30
/**
31
* Try SSL connection first (default).
32
* If server doesn't support SSL, fallback to non-SSL.
33
* Provides encryption if available.
34
*/
35
PREFER,
36
37
/**
38
* Require SSL connection.
39
* Fail if server doesn't support SSL.
40
* Does NOT verify server certificate.
41
*/
42
REQUIRE,
43
44
/**
45
* Require SSL with server certificate verification.
46
* Verifies certificate is signed by trusted CA.
47
* Does NOT verify hostname.
48
*/
49
VERIFY_CA,
50
51
/**
52
* Require SSL with full certificate verification.
53
* Verifies certificate AND hostname match.
54
* Most secure mode.
55
*/
56
VERIFY_FULL;
57
58
/**
59
* Returns whether this mode requires an encrypted connection.
60
*
61
* @return true if REQUIRE or higher (VERIFY_CA, VERIFY_FULL)
62
*/
63
public boolean requireEncryption();
64
65
/**
66
* Returns whether this mode verifies the server certificate.
67
*
68
* @return true for VERIFY_CA or VERIFY_FULL
69
*/
70
public boolean verifyCertificate();
71
72
/**
73
* Returns whether this mode verifies the server hostname matches the certificate.
74
*
75
* @return true only for VERIFY_FULL
76
*/
77
public boolean verifyPeerName();
78
79
/**
80
* Parses SSL mode from connection properties.
81
* Falls back to checking the 'ssl' property if 'sslmode' is not set.
82
*
83
* @param info Connection properties
84
* @return SslMode enum constant
85
* @throws PSQLException if sslmode value is invalid
86
*/
87
public static SslMode of(Properties info) throws PSQLException;
88
}
89
```
90
91
### GSSAPI Encryption Modes
92
93
PostgreSQL JDBC driver supports GSSAPI encryption (Kerberos-based encryption) as an alternative to SSL.
94
95
```java { .api }
96
package org.postgresql.jdbc;
97
98
/**
99
* GSSAPI encryption modes for Kerberos-based secure connections.
100
* Provides an alternative to SSL for environments using Kerberos authentication.
101
*/
102
public enum GSSEncMode {
103
/**
104
* Do not use GSSAPI encrypted connections.
105
*/
106
DISABLE,
107
108
/**
109
* Start with non-encrypted connection, then try encrypted one.
110
*/
111
ALLOW,
112
113
/**
114
* Start with encrypted connection, fallback to non-encrypted (default).
115
*/
116
PREFER,
117
118
/**
119
* Ensure connection is encrypted via GSSAPI.
120
*/
121
REQUIRE;
122
123
/**
124
* Returns whether this mode requires GSSAPI encryption.
125
*
126
* @return true if REQUIRE mode is set
127
*/
128
public boolean requireEncryption();
129
130
/**
131
* Parses GSSAPI encryption mode from connection properties.
132
* Falls back to ALLOW if gssEncMode is not set.
133
*
134
* @param info Connection properties
135
* @return GSSEncMode enum constant
136
* @throws PSQLException if gssEncMode value is invalid
137
*/
138
public static GSSEncMode of(Properties info) throws PSQLException;
139
}
140
```
141
142
**Configuration Examples:**
143
144
```java
145
import java.sql.Connection;
146
import java.sql.DriverManager;
147
import java.util.Properties;
148
149
// Example 1: SSL via URL
150
public class SSLURLExample {
151
public static Connection connectSSL() throws SQLException {
152
String url = "jdbc:postgresql://localhost:5432/mydb?ssl=true&sslmode=require";
153
return DriverManager.getConnection(url, "user", "password");
154
}
155
}
156
157
// Example 2: SSL via Properties
158
public class SSLPropertiesExample {
159
public static Connection connectSSL() throws SQLException {
160
String url = "jdbc:postgresql://localhost:5432/mydb";
161
Properties props = new Properties();
162
props.setProperty("user", "postgres");
163
props.setProperty("password", "secret");
164
props.setProperty("ssl", "true");
165
props.setProperty("sslmode", "verify-full");
166
return DriverManager.getConnection(url, props);
167
}
168
}
169
170
// Example 3: SSL via DataSource
171
public class SSLDataSourceExample {
172
public static Connection connectSSL() throws SQLException {
173
PGSimpleDataSource ds = new PGSimpleDataSource();
174
ds.setServerName("db.example.com");
175
ds.setDatabaseName("mydb");
176
ds.setUser("postgres");
177
ds.setPassword("secret");
178
ds.setSsl(true);
179
ds.setSslMode("verify-full");
180
return ds.getConnection();
181
}
182
}
183
```
184
185
### SSL Factories
186
187
Custom SSL socket factories for certificate management.
188
189
```java { .api }
190
package org.postgresql.ssl;
191
192
import javax.net.ssl.SSLSocketFactory;
193
194
/**
195
* Default SSL factory compatible with libpq certificate file locations.
196
* Looks for certificates in ~/.postgresql/ directory:
197
* - root.crt: trusted CA certificates
198
* - postgresql.crt: client certificate
199
* - postgresql.pk8: client private key (PKCS#8 format)
200
*/
201
public class LibPQFactory implements SSLSocketFactory {
202
// Automatically loads certificates from standard locations
203
}
204
205
/**
206
* SSL factory that does NOT validate server certificates.
207
* WARNING: Vulnerable to man-in-the-middle attacks.
208
* Only use for testing or when security is not required.
209
*/
210
public class NonValidatingFactory extends WrappedFactory {
211
/**
212
* Constructor accepting an optional argument.
213
* The argument is ignored but presence avoids reflection lookup overhead.
214
*
215
* @param arg Optional argument (ignored)
216
* @throws GeneralSecurityException if SSL context cannot be created
217
*/
218
public NonValidatingFactory(String arg) throws GeneralSecurityException;
219
}
220
221
/**
222
* SSL factory that validates against a single trusted certificate.
223
* More secure than NonValidatingFactory as it pins the server certificate.
224
* The certificate can be loaded from classpath, filesystem, environment variable, or inline.
225
*
226
* Configuration via sslfactoryarg connection property:
227
* - classpath:ssl/server.crt - Load from classpath
228
* - file:/path/to/server.crt - Load from filesystem
229
* - env:MYDB_CERT - Load from environment variable
230
* - sys:mydb.cert - Load from system property
231
* - No prefix - Inline PEM-encoded certificate
232
*/
233
public class SingleCertValidatingFactory extends WrappedFactory {
234
/**
235
* Constructor accepting certificate specification via sslfactoryarg.
236
*
237
* @param arg Certificate source specification (see class documentation)
238
* @throws GeneralSecurityException if certificate cannot be loaded or validated
239
*/
240
public SingleCertValidatingFactory(String arg) throws GeneralSecurityException;
241
}
242
243
/**
244
* SSL factory using Java's default SSL configuration.
245
* Uses system truststore (cacerts) and keystore.
246
* Always validates server certificate using Java's default truststore.
247
*/
248
public class DefaultJavaSSLFactory extends WrappedFactory {
249
/**
250
* Constructor using Java's default SSL socket factory.
251
*
252
* @param info Connection properties (unused)
253
*/
254
public DefaultJavaSSLFactory(Properties info);
255
}
256
257
/**
258
* Abstract base class for SSL factories using JKS keystore.
259
* Subclasses must implement getKeyStorePassword() and getKeyStoreStream().
260
* The keystore is used for both client certificates and trusted CAs.
261
*/
262
public abstract class DbKeyStoreSocketFactory extends WrappedFactory {
263
/**
264
* Constructor that loads and initializes the keystore.
265
*
266
* @throws DbKeyStoreSocketException if keystore cannot be loaded
267
*/
268
public DbKeyStoreSocketFactory() throws DbKeyStoreSocketException;
269
270
/**
271
* Returns the password for the keystore.
272
* Must be implemented by subclasses.
273
*
274
* @return Keystore password as char array
275
*/
276
public abstract char[] getKeyStorePassword();
277
278
/**
279
* Returns an InputStream for reading the keystore.
280
* Must be implemented by subclasses.
281
*
282
* @return InputStream for JKS keystore
283
*/
284
public abstract InputStream getKeyStoreStream();
285
286
/**
287
* Exception thrown when keystore operations fail.
288
*/
289
public static class DbKeyStoreSocketException extends Exception {
290
public DbKeyStoreSocketException(String message);
291
}
292
}
293
294
/**
295
* Custom hostname verifier for SSL connections.
296
*/
297
public class PGjdbcHostnameVerifier implements javax.net.ssl.HostnameVerifier {
298
@Override
299
public boolean verify(String hostname, javax.net.ssl.SSLSession session);
300
}
301
```
302
303
**Configuration Examples:**
304
305
```java
306
// Example 1: Using LibPQFactory (default)
307
public class LibPQFactoryExample {
308
public static Connection connectWithLibPQ() throws SQLException {
309
// Place certificates in ~/.postgresql/:
310
// - root.crt (trusted CA certificates)
311
// - postgresql.crt (client certificate)
312
// - postgresql.pk8 (client private key)
313
314
Properties props = new Properties();
315
props.setProperty("user", "postgres");
316
props.setProperty("password", "secret");
317
props.setProperty("ssl", "true");
318
props.setProperty("sslmode", "verify-full");
319
props.setProperty("sslfactory", "org.postgresql.ssl.LibPQFactory");
320
321
String url = "jdbc:postgresql://localhost/mydb";
322
return DriverManager.getConnection(url, props);
323
}
324
}
325
326
// Example 2: Using DefaultJavaSSLFactory
327
public class JavaSSLFactoryExample {
328
public static Connection connectWithJavaSSL() throws SQLException {
329
// Set JVM properties for truststore/keystore
330
System.setProperty("javax.net.ssl.trustStore", "/path/to/truststore.jks");
331
System.setProperty("javax.net.ssl.trustStorePassword", "password");
332
System.setProperty("javax.net.ssl.keyStore", "/path/to/keystore.jks");
333
System.setProperty("javax.net.ssl.keyStorePassword", "password");
334
335
Properties props = new Properties();
336
props.setProperty("user", "postgres");
337
props.setProperty("password", "secret");
338
props.setProperty("ssl", "true");
339
props.setProperty("sslmode", "verify-full");
340
props.setProperty("sslfactory", "org.postgresql.ssl.DefaultJavaSSLFactory");
341
342
String url = "jdbc:postgresql://localhost/mydb";
343
return DriverManager.getConnection(url, props);
344
}
345
}
346
347
// Example 3: NonValidatingFactory (testing only)
348
public class NonValidatingExample {
349
public static Connection connectWithoutValidation() throws SQLException {
350
// WARNING: Do not use in production!
351
Properties props = new Properties();
352
props.setProperty("user", "postgres");
353
props.setProperty("password", "secret");
354
props.setProperty("ssl", "true");
355
props.setProperty("sslfactory", "org.postgresql.ssl.NonValidatingFactory");
356
357
String url = "jdbc:postgresql://localhost/mydb";
358
return DriverManager.getConnection(url, props);
359
}
360
}
361
```
362
363
### Password Management
364
365
PostgreSQL JDBC driver provides a secure method to change user passwords.
366
367
```java { .api }
368
package org.postgresql;
369
370
/**
371
* Secure password change functionality (part of PGConnection interface).
372
*/
373
public interface PGConnection extends Connection {
374
/**
375
* Change a user's password to the specified new password.
376
*
377
* The password is encrypted locally before transmission, so the plain text
378
* password is never sent on the wire. This provides secure password changes
379
* even over non-SSL connections.
380
*
381
* If encryptionType is null, this method queries the database server for
382
* the server's default password_encryption setting.
383
*
384
* The newPassword array is automatically zeroed out after use for security.
385
*
386
* @param user The username of the database user
387
* @param newPassword The new password (will be zeroed after use)
388
* @param encryptionType Encryption type: "md5", "scram-sha-256", or null for server default
389
* @throws SQLException If the password could not be altered
390
* @since 42.2.0
391
*/
392
default void alterUserPassword(String user, char[] newPassword, String encryptionType)
393
throws SQLException;
394
}
395
```
396
397
**Usage Example:**
398
399
```java
400
import org.postgresql.PGConnection;
401
import java.sql.*;
402
403
public class PasswordManagementExample {
404
public static void changePassword(Connection conn, String username) throws SQLException {
405
// Unwrap to get PGConnection interface
406
PGConnection pgConn = conn.unwrap(PGConnection.class);
407
408
// New password as char array (more secure than String)
409
char[] newPassword = "newSecurePassword123!".toCharArray();
410
411
try {
412
// Use SCRAM-SHA-256 for maximum security (PostgreSQL 10+)
413
pgConn.alterUserPassword(username, newPassword, "scram-sha-256");
414
System.out.println("Password changed successfully for user: " + username);
415
} finally {
416
// Password array is automatically zeroed by the method
417
// No need to manually zero it
418
}
419
}
420
421
public static void changePasswordWithServerDefault(Connection conn, String username)
422
throws SQLException {
423
PGConnection pgConn = conn.unwrap(PGConnection.class);
424
425
char[] newPassword = "anotherSecurePassword!".toCharArray();
426
427
// Use null to let server decide encryption type (recommended)
428
pgConn.alterUserPassword(username, newPassword, null);
429
System.out.println("Password changed using server default encryption");
430
}
431
}
432
```
433
434
**Important Notes:**
435
436
- **Use `null` for encryptionType**: Recommended to use the server's default
437
- **Avoid "md5"**: Only use for legacy servers that don't support SCRAM-SHA-256
438
- **Use "scram-sha-256"**: Most secure option for PostgreSQL 10+
439
- **char[] vs String**: Password is passed as char[] for security (can be zeroed)
440
- **Automatic cleanup**: The method automatically zeros the password array after use
441
- **Local encryption**: Password is encrypted locally, never sent in plain text
442
- **Permissions required**: User must have ALTER USER privilege or be superuser
443
444
### Certificate Configuration
445
446
**Connection Properties:**
447
448
```java { .api }
449
/**
450
* SSL-related connection properties
451
*/
452
public class SSLProperties {
453
/**
454
* ssl - Enable SSL connection
455
* Values: true, false
456
* Default: false
457
*/
458
String ssl;
459
460
/**
461
* sslmode - SSL security mode
462
* Values: disable, allow, prefer, require, verify-ca, verify-full
463
* Default: prefer
464
*/
465
String sslmode;
466
467
/**
468
* sslfactory - SSL socket factory class
469
* Default: org.postgresql.ssl.LibPQFactory
470
*/
471
String sslfactory;
472
473
/**
474
* sslcert - Client certificate file path
475
* Default: ~/.postgresql/postgresql.crt
476
*/
477
String sslcert;
478
479
/**
480
* sslkey - Client private key file path
481
* Default: ~/.postgresql/postgresql.pk8
482
*/
483
String sslkey;
484
485
/**
486
* sslrootcert - Root certificate file path (trusted CAs)
487
* Default: ~/.postgresql/root.crt
488
*/
489
String sslrootcert;
490
491
/**
492
* sslpassword - Password for encrypted private key
493
*/
494
String sslpassword;
495
496
/**
497
* sslpasswordcallback - Callback class for password
498
*/
499
String sslpasswordcallback;
500
501
/**
502
* sslhostnameverifier - Custom hostname verifier class
503
*/
504
String sslhostnameverifier;
505
506
/**
507
* sslfactoryarg - Argument passed to SSL factory
508
*/
509
String sslfactoryarg;
510
}
511
```
512
513
**Usage Examples:**
514
515
```java
516
// Example 1: Custom certificate locations
517
public class CustomCertificates {
518
public static Connection connectWithCustomCerts() throws SQLException {
519
Properties props = new Properties();
520
props.setProperty("user", "postgres");
521
props.setProperty("password", "secret");
522
props.setProperty("ssl", "true");
523
props.setProperty("sslmode", "verify-full");
524
props.setProperty("sslcert", "/path/to/client.crt");
525
props.setProperty("sslkey", "/path/to/client.pk8");
526
props.setProperty("sslrootcert", "/path/to/ca.crt");
527
528
String url = "jdbc:postgresql://db.example.com/mydb";
529
return DriverManager.getConnection(url, props);
530
}
531
}
532
533
// Example 2: Encrypted private key
534
public class EncryptedKey {
535
public static Connection connectWithEncryptedKey() throws SQLException {
536
Properties props = new Properties();
537
props.setProperty("user", "postgres");
538
props.setProperty("password", "secret");
539
props.setProperty("ssl", "true");
540
props.setProperty("sslmode", "verify-full");
541
props.setProperty("sslkey", "/path/to/encrypted.pk8");
542
props.setProperty("sslpassword", "key-password");
543
544
String url = "jdbc:postgresql://localhost/mydb";
545
return DriverManager.getConnection(url, props);
546
}
547
}
548
```
549
550
### Certificate Formats
551
552
**Supported Formats:**
553
554
1. **Client Certificate**: X.509 PEM format (.crt, .pem)
555
```
556
-----BEGIN CERTIFICATE-----
557
...certificate data...
558
-----END CERTIFICATE-----
559
```
560
561
2. **Private Key**: PKCS#8 unencrypted or encrypted format (.pk8, .key)
562
```
563
-----BEGIN PRIVATE KEY-----
564
...key data...
565
-----END PRIVATE KEY-----
566
```
567
568
3. **Root Certificate**: X.509 PEM format with one or more CA certificates
569
```
570
-----BEGIN CERTIFICATE-----
571
...CA cert 1...
572
-----END CERTIFICATE-----
573
-----BEGIN CERTIFICATE-----
574
...CA cert 2...
575
-----END CERTIFICATE-----
576
```
577
578
**Converting Certificates:**
579
580
```bash
581
# Convert PKCS#1 to PKCS#8 (required format)
582
openssl pkcs8 -topk8 -inform PEM -outform PEM -in postgresql.key -out postgresql.pk8 -nocrypt
583
584
# Convert PKCS#12 to PEM
585
openssl pkcs12 -in cert.p12 -out postgresql.crt -clcerts -nokeys
586
openssl pkcs12 -in cert.p12 -out postgresql.key -nocerts -nodes
587
588
# Extract CA certificate
589
openssl s_client -connect db.example.com:5432 -starttls postgres < /dev/null | openssl x509 > root.crt
590
```
591
592
### Custom SSL Factory
593
594
Implementing a custom SSL factory:
595
596
```java
597
import javax.net.ssl.*;
598
import java.security.KeyStore;
599
import java.io.FileInputStream;
600
601
public class CustomSSLFactory extends SSLSocketFactory {
602
private SSLSocketFactory factory;
603
604
public CustomSSLFactory() throws Exception {
605
// Load truststore
606
KeyStore trustStore = KeyStore.getInstance("JKS");
607
try (FileInputStream fis = new FileInputStream("/path/to/truststore.jks")) {
608
trustStore.load(fis, "password".toCharArray());
609
}
610
611
// Create TrustManager
612
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
613
TrustManagerFactory.getDefaultAlgorithm());
614
tmf.init(trustStore);
615
616
// Create SSLContext
617
SSLContext ctx = SSLContext.getInstance("TLS");
618
ctx.init(null, tmf.getTrustManagers(), null);
619
620
factory = ctx.getSocketFactory();
621
}
622
623
// Implement SSLSocketFactory methods delegating to factory
624
@Override
625
public Socket createSocket(String host, int port) throws IOException {
626
return factory.createSocket(host, port);
627
}
628
629
// ... other methods
630
}
631
632
// Usage
633
Properties props = new Properties();
634
props.setProperty("ssl", "true");
635
props.setProperty("sslfactory", "com.example.CustomSSLFactory");
636
```
637
638
### Security Best Practices
639
640
1. **Always use SSL in production**
641
```java
642
props.setProperty("ssl", "true");
643
props.setProperty("sslmode", "verify-full");
644
```
645
646
2. **Verify server certificates**
647
```
648
Use verify-full mode to prevent MITM attacks
649
```
650
651
3. **Protect private keys**
652
```
653
- Use file permissions (chmod 600)
654
- Encrypt keys with passwords
655
- Store in secure locations
656
```
657
658
4. **Use strong cipher suites**
659
```java
660
// Disable weak ciphers via JVM properties
661
System.setProperty("jdk.tls.disabledAlgorithms",
662
"SSLv3, RC4, DES, MD5withRSA, DH keySize < 2048");
663
```
664
665
5. **Rotate certificates regularly**
666
```
667
Implement certificate expiry monitoring
668
```
669
670
6. **Use certificate pinning for critical systems**
671
```java
672
// Implement custom SSLFactory with certificate pinning
673
```
674
675
7. **Monitor SSL configuration**
676
```java
677
// Log SSL connection details
678
Logger logger = Logger.getLogger("org.postgresql");
679
logger.setLevel(Level.FINE);
680
```
681
682
### Common SSL Configurations
683
684
**Development (localhost):**
685
```properties
686
ssl=false
687
```
688
689
**Testing (self-signed certs):**
690
```properties
691
ssl=true
692
sslmode=require
693
sslfactory=org.postgresql.ssl.NonValidatingFactory
694
```
695
696
**Production (CA-signed certs):**
697
```properties
698
ssl=true
699
sslmode=verify-full
700
sslrootcert=/path/to/ca-bundle.crt
701
```
702
703
**Production (client certificates):**
704
```properties
705
ssl=true
706
sslmode=verify-full
707
sslcert=/path/to/client.crt
708
sslkey=/path/to/client.pk8
709
sslrootcert=/path/to/ca-bundle.crt
710
```
711
712
**Cloud Providers (AWS RDS, Azure, GCP):**
713
```properties
714
ssl=true
715
sslmode=verify-full
716
# Download provider's CA certificate
717
sslrootcert=/path/to/provider-ca.crt
718
```
719