Subject: [pgjdbc/pgjdbc] issue #3349: SSL connection is not established

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Mon, 19 Aug 2024 10:53:49 +0000

I am getting below exception when I am trying to connect to postgres using ssl. I am connecting by using root.crt file and verify-full mode.

`Caused by: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors,  at java.base/sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:369),  at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:275),  at java.base/sun.security.validator.Validator.validate(Validator.java:264),  at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231),  at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132),  at java.base/sun.security.ssl.CertificateMessage$T13CertificateConsumer.checkServerCerts(CertificateMessage.java:1341),  ... 84 common frames omittedCaused by: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors,  at java.base/sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:157),  at java.base/sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:83),  at java.base/java.security.cert.CertPathValidator.validate(CertPathValidator.java:309),  at java.base/sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:364),  ... 89 common frames omitted`

@davecramer Please let me know what can be the issue ?

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Mon, 19 Aug 2024 14:03:16 +0000

did you follow https://jdbc.postgresql.org/documentation/ssl/

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Tue, 20 Aug 2024 05:06:22 +0000

@davecramer Please see below parameters used.
SSL =true ;
SSL_MODE= verify-full;
SSL_RESPONSE_TIMEOUT =5000;
SSL_ROOT_CERT = root crt path

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Tue, 20 Aug 2024 09:05:06 +0000

Hi @Shalaka1197 

If you are doing verify full then you also need the client certificates.

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Wed, 21 Aug 2024 08:17:09 +0000

@davecramer in which mode root.crt file verification is done ?
In the documentation this is not specified point - "verify full then you also need the client certificates"
https://jdbc.postgresql.org/documentation/ssl/

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Wed, 21 Aug 2024 14:53:02 +0000

The server `root.crt` will be verified on every SSL connection

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Thu, 22 Aug 2024 19:31:26 +0000

Is there any test cases for expired root.crt server.crt files ?
How is it validated from postgres end ? @davecramer

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Thu, 22 Aug 2024 19:31:59 +0000

@davecramer How to check the connection is made with ssl or without ssl in postgres ?

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Thu, 22 Aug 2024 19:51:43 +0000

install the sslinfo contrib module https://www.postgresql.org/docs/current/sslinfo.html#SSLINFO

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Fri, 23 Aug 2024 05:06:22 +0000

@davecramer for checking the validity of server and root certificate is there any use case  or test case ?

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Fri, 23 Aug 2024 10:41:07 +0000

You can just use openssl to verify your certs

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Fri, 23 Aug 2024 12:54:00 +0000

@davecramer From postgres code is there any mechanism to check certificate expiry for root.crt file and client.crt files ?
It is connecting for expired certificates as well.
Its seems to be bug from postgres side.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Fri, 23 Aug 2024 14:52:13 +0000

https://www.enterprisedb.com/blog/authentication-ssl-client-certificates#:~:text=If%20the%20authentication%20mechanism%20in%20your%20pg_hba.conf%20file,that%20authentication%20method%2C%20giving%20you%20two%20factor%20authentication.

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Thu, 29 Aug 2024 03:59:24 +0000

@davecramer Please see below use case of mine.
I am trying to connect with the postgres via one way tls protocol(only root.crt file verified).
so from postgres server side we have configured the server.crt file and root.crt file and its path is given under the postgresql.conf file.
Now I am trying to connect to server application via root.crt file from client application. The below is code snippet.
`
SSL =true ;
SSL_MODE= verify-ca;
SSL_RESPONSE_TIMEOUT =5000;
SSL_ROOT_CERT = /tmp/server.crt
`
for ssl root cert parameter we are passing server.crt as it is giving the issues when I passed root.crt file. It is able to connect via client application as well using server.crt file. 
Now for negative test case scenario, I have created server.crt file with 1 day expiry and passed the same file in ssl_root_cert parameter , after one day the certificate is expired for server.crt file .
>From psql command part below is the error we got.
**PSQL error for expired server.crt file -   certificate verification failed.
java application - its able to connect via  expired server.crt file**

Now I am given root.crt file for ssl_root_cert parameter and got the below error.
`
Caused by: java.security.cert.CertificateExpiredException: NotAfter: Fri Aug 23 11:44:37 UTC 2024,  at java.base/sun.security.x509.CertificateValidity.valid(CertificateValidity.java:277)
,  at java.base/sun.security.x509.X509CertImpl.checkValidity(X509CertImpl.java:627)
,  at java.base/sun.security.provider.certpath.BasicChecker.verifyValidity(BasicChecker.java:190)
,  at java.base/sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:144)
`

Now please tell me why server.crt file is able to connect via postgres even its certificate is expired ?
root.crt file is giving error and server.crt file also should throw the same error right ?

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Thu, 29 Aug 2024 11:35:33 +0000

I would be happy to look into this for you.

Please provide detailed instructions on how to replicate your problem

Cheers,
Dave

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Fri, 30 Aug 2024 06:27:44 +0000

@davecramer 
When you are creating root.crt and server.crt file , try to create it with 0 days validity.
for example command :
`
openssl req -new -x509 -days 0 -nodes -text -out server.crt -keyout server.key -subj "/CN=localhost"
`

and put both the certificates in postgres server.
Below is sample code for replicating the issue.

ssl: true
sslmode:verify-ca
sslrootcert: resources/server.crt

public class ConnectDB {
	
	private final String url = "jdbc:postgresql://abcd:5432/postgres?" +
            "sslmode=verify-ca&sslrootcert=resources/server.crt";

    private final String user = "postgres";
    private final String password = "abcd";

    public Connection connect() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, user, password);
            System.out.println("Connected to the PostgreSQL server successfully.");
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }

        return conn;
    }

	public static void main(String[] args) {
		
		ConnectDB db = new ConnectDB();
		db.connect();

	}
`

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Fri, 30 Aug 2024 12:31:04 +0000

Thanks, I was able to replicate this issue.

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Fri, 30 Aug 2024 16:44:50 +0000

@davecramer Looking for support from postgres end for this issue.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Sat, 31 Aug 2024 11:35:43 +0000

@Shalaka1197 I don't think this is a server issue.
Turns out that java doesn't check the end date of the certificate

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Mon, 02 Sep 2024 11:18:20 +0000

@davecramer But for root.crt file its checking validity and throwing certificate error. Why is it not happening server.crt file ?
For java its doesn't check validity do you have any reference links for the same ?

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Mon, 02 Sep 2024 11:47:13 +0000

https://security.stackexchange.com/questions/105637/does-trustmanager-pkix-or-rfc-3280-x-509-really-check-the-expiration-date-of-a

---

From: "Shalaka1197 (@Shalaka1197)" <[email-masked]>
Date: Tue, 03 Sep 2024 05:30:55 +0000

@davecramer How is it checking expiry for root.crt files and throwing below exception ? Why is it not applicable for server.crt file ?

`Caused by: java.security.cert.CertificateExpiredException: NotAfter: Fri Aug 23 11:44:37 UTC 2024,  at java.base/sun.security.x509.CertificateValidity.valid(CertificateValidity.java:277) ,  at java.base/sun.security.x509.X509CertImpl.checkValidity(X509CertImpl.java:627) ,  at java.base/sun.security.provider.certpath.BasicChecker.verifyValidity(BasicChecker.java:190) ,  at java.base/sun.security.provider.certpath.BasicChecker.check(BasicChecker.java:144)`

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Tue, 03 Sep 2024 11:27:28 +0000

I am on vacation and will be back next week.

---

From: "kaushalaroraharman (@kaushalaroraharman)" <[email-masked]>
Date: Mon, 04 Nov 2024 08:18:37 +0000

@davecramer Any update on this issue?

---

From: "kaushalaroraharman (@kaushalaroraharman)" <[email-masked]>
Date: Thu, 08 May 2025 07:25:32 +0000

We are still facing this issue. Please provide an update on when this can be resolved.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Thu, 08 May 2025 10:31:42 +0000

@kaushalaroraharman I don't have any updates. Can you remind me what exactly the issue is ?

---

From: "kaushalaroraharman (@kaushalaroraharman)" <[email-masked]>
Date: Thu, 08 May 2025 11:13:44 +0000

@davecramer A brief of the issue is that even with an expired certificate, client is able to connect to Postgres. The expectation is that with an expired certificate, connection should be refused.

Please go through https://github.com/pgjdbc/pgjdbc/issues/3349#issuecomment-2320206340, the previous comments and description for details on the issue.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Thu, 08 May 2025 11:29:20 +0000

As I mentioned previously the default trustmanager does not check the expiry date https://security.stackexchange.com/questions/105637/does-trustmanager-pkix-or-rfc-3280-x-509-really-check-the-expiration-date-of-a

If you want to enforce it you may want to implement a different trustmanager

---

From: "kaushalaroraharman (@kaushalaroraharman)" <[email-masked]>
Date: Thu, 08 May 2025 12:06:23 +0000

@davecramer Yes, I have went through this. But the question remains, how it is working when root certificate is expired? The same which was also highlighted under [comment#](https://github.com/pgjdbc/pgjdbc/issues/3349#issuecomment-2325634390). Can you please share some inputs on this?

---

From: "sehrope (@sehrope)" <[email-masked]>
Date: Fri, 09 May 2025 14:58:20 +0000

It works when the certificate is expired because the default TrustManager used to validate the certificate does not check the certificate expiration. It only checks if server's certificate matches or is signed by the `root.crt`.

This is due to the implementations of TrustManager not including that check in their default validation. That's part of the JDK classes, it's not specific to this driver, and . It's possible that could change in the future, but I doubt it will ever happen as it would break a lot of legacy applications.

The rationale for this is because once you've added a certificate to the trust store, it is assumed that you trust it. The trust store itself does not subsequently check if the certificate is expired or invalidated. That is supposed to happen at a higher level prior to creation of the trust store. For a long running application there's the additional complexity of the application "breaking" after it has been online.

We could consider adding such an "is expired" check to the PGJDBC driver's `LibPQFactory` (the default that performs most TLS validation) when it reads the certificates from `root.crt`, however I think it'd have to be optional / opt-in. Changing that default to reject expired certificates would definitely break existing application, in particular the legacy ones that would be more likely to have such expired certificates.

In the meantime if you want to ensure that expired certificates are rejected, you can create your own SSLSocketFactory that clones the behavior in `LibPQFactory` with the extra expiration check by invoking [`checkValidity()`](https://docs.oracle.com/javase/8/docs/api/java/security/cert/X509Certificate.html#checkValidity--) on each certificate before as it is [added to the list](https://github.com/pgjdbc/pgjdbc/blob/d9e20874590f59543c39a99b824e09344f00a813/pgjdbc/src/main/java/org/postgresql/ssl/LibPQFactory.java#L161).

---

From: "kaushalaroraharman (@kaushalaroraharman)" <[email-masked]>
Date: Fri, 09 May 2025 19:03:35 +0000

@sehrope Thank you for sharing the detailed explanation. It helped me understand the cause of the issue clearly. I will look into the custom SSLSocketFactory as suggested.