pgjdbc: Error validating client certificate: PostgreSQL reports "ccs received early" error

**I’m submitting a **

  • bug report
  • feature request

Describe the issue

I’m passing in a client certificate, client key, and certificate authorities using properties sslrootcert, sslcert, and sslkey in the JDBC driver and am receiving the SSL error:

could not accept SSL connection: ccs received early

This occurs at the client - Change Cipher Spec record during the SSL handshake.

Passing the same certificates into psql:

psql "host=localhost dbname=training user=mule password=mule sslmode=verify-ca sslrootcert=root-client.crt sslcert=client.crt sslkey=client.key"

works. This means I know the certificates themselves are not the issue.

Driver Version? 42.2.5

Java Version? Oracle JDK 1.8.0_191

OS Version? Mac OS 10.13.6

PostgreSQL Version? 10.6

To Reproduce

  1. Create self-signed certificates for both server and client using openssl.
  2. Install server certificates, key, and client certificate into CA authority certificate.
  3. Configure pg_hba.conf for hostssl:
hostssl    all             all             127.0.0.1/32           md5
hostssl    all             all             ::1/128                    md5
  1. Setup a database, a table, and a user account with permissions to the table in the PostgreSQL.
  2. Confirm psql works with provided client certificate, key and server certificate as the sslrootcert:
psql "host=localhost dbname=training user=mule password=mule sslmode=verify-ca sslrootcert=root-client.crt sslcert=client.crt sslkey=client.key"
  1. Use the JDBC driver in a Java program to access the same table with the same credentials and certificates:
import java.sql.*;
import java.util.Properties;

public class CCS {
    //Test for ccs received early bug
    public static void main(String[] args) throws java.sql.SQLException {
        String url = "jdbc:postgresql://localhost:5432/training";

        Properties props = new Properties();
        props.setProperty("user","mule");
        props.setProperty("password","mule");
        props.setProperty("sslmode","verify-ca");
        props.setProperty("sslrootcert","/Users/mark/certs/root-client.crt");
        props.setProperty("sslcert","/Users/mark/certs/client.crt");
        props.setProperty("sslkey","/Users/mark/certs/client.key");
        props.setProperty("loggerLevel","TRACE");
        props.setProperty("loggerFile","pgjdbc-trace.log");

        Connection conn = DriverManager.getConnection(url,props);
        Statement st = conn.createStatement();
        ResultSet rs = st.executeQuery("SELECT username FROM test_accounts");

        System.out.println("Column: username");
        while (rs.next()) {
            System.out.println(rs.getString(1));
        }

        rs.close();
        st.close();
        conn.close();
    }
}
  1. javax.net.ssl.SSLException: Received fatal alert: unexpected_message gets thrown when running the Java program.

Expected behaviour The SSL handshake should complete successfully and application data gets transferred to the Java program.

And what actually happens:

Exception gets thrown: javax.net.ssl.SSLException: Received fatal alert: unexpected_message

Logs

JDBC Driver Logs

Dec 06, 2018 10:14:30 AM org.postgresql.Driver connect
FINE: Connecting with URL: jdbc:postgresql://localhost:5432/training
Dec 06, 2018 10:14:30 AM org.postgresql.jdbc.PgConnection <init>
FINE: PostgreSQL JDBC Driver 42.2.5
Dec 06, 2018 10:14:30 AM org.postgresql.jdbc.PgConnection setDefaultFetchSize
FINE:   setDefaultFetchSize = 0
Dec 06, 2018 10:14:30 AM org.postgresql.jdbc.PgConnection setPrepareThreshold
FINE:   setPrepareThreshold = 5
Dec 06, 2018 10:14:30 AM org.postgresql.core.v3.ConnectionFactoryImpl openConnectionImpl
FINE: Trying to establish a protocol version 3 connection to localhost:5432
Dec 06, 2018 10:14:30 AM org.postgresql.core.Encoding <init>
FINEST: Creating new Encoding UTF-8 with fastASCIINumbers true
Dec 06, 2018 10:14:30 AM org.postgresql.core.Encoding <init>
FINEST: Creating new Encoding UTF-8 with fastASCIINumbers true
Dec 06, 2018 10:14:30 AM org.postgresql.core.Encoding <init>
FINEST: Creating new Encoding UTF-8 with fastASCIINumbers true
Dec 06, 2018 10:14:30 AM org.postgresql.core.v3.ConnectionFactoryImpl enableSSL
FINEST:  FE=> SSLRequest
Dec 06, 2018 10:14:30 AM org.postgresql.core.v3.ConnectionFactoryImpl enableSSL
FINEST:  <=BE SSLOk
Dec 06, 2018 10:14:30 AM org.postgresql.ssl.MakeSSL convert
FINE: converting regular socket connection to ssl
Dec 06, 2018 10:14:30 AM org.postgresql.Driver connect
FINE: Connection error: 
org.postgresql.util.PSQLException: SSL error: Received fatal alert: unexpected_message
	at org.postgresql.ssl.MakeSSL.convert(MakeSSL.java:42)
	at org.postgresql.core.v3.ConnectionFactoryImpl.enableSSL(ConnectionFactoryImpl.java:435)
	at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:94)
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:192)
	at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
	at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195)
	at org.postgresql.Driver.makeConnection(Driver.java:454)
	at org.postgresql.Driver.connect(Driver.java:256)
	at java.sql.DriverManager.getConnection(DriverManager.java:664)
	at java.sql.DriverManager.getConnection(DriverManager.java:208)
	at CCS.main(CCS.java:19)
Caused by: javax.net.ssl.SSLException: Received fatal alert: unexpected_message
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
	at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2020)
	at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1127)
	at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
	at org.postgresql.ssl.MakeSSL.convert(MakeSSL.java:40)
	... 10 more

PostGres Log

2018-12-06 18:14:30.661 UTC [89527] LOG:  could not accept SSL connection: ccs received early

javax.net.debug = ssl:handshake Log

For brevity, I’m only including the step before Client Change Cipher Spec.

main, WRITE: TLSv1.2 Handshake, length = 1057
SESSION KEYGEN:
PreMaster Secret:
0000: 91 BB 64 B1 F8 D5 93 91   81 04 43 3D 15 BD 5A 5C  ..d.......C=..Z\
0010: 21 2D 6E 93 E3 1C F1 65   EF 3D 6D 52 15 BA FC 42  !-n....e.=mR...B
CONNECTION KEYGEN:
Client Nonce:
0000: 5C 09 68 10 D8 A3 D6 70   FF 90 2D 04 7B C0 E1 DC  \.h....p..-.....
0010: C8 87 D6 25 72 F2 BD B0   8C 78 73 1A 72 C1 49 16  ...%r....xs.r.I.
Server Nonce:
0000: 7B 88 53 D6 85 3F 3F 9F   FE 90 2C E2 1A C6 22 11  ..S..??...,...".
0010: 43 B6 1B A0 D8 FE 58 B6   31 83 DE 81 60 F5 29 92  C.....X.1...`.).
Master Secret:
0000: E2 42 BD 0E 8B AF 14 63   75 35 FA 25 FA BF 22 8D  .B.....cu5.%..".
0010: 03 A1 A7 6F 3B 24 68 49   91 FA D3 5B 46 98 C6 CC  ...o;$hI...[F...
0020: C5 CE 0D C4 98 4F 6F 22   50 ED E5 62 3D E1 F2 EC  .....Oo"P..b=...
... no MAC keys used for this cipher
Client write key:
0000: EA 49 03 97 1E B4 2B 53   E7 81 9A 66 7D 5A 59 E5  .I....+S...f.ZY.
0010: A8 D3 29 57 2E A0 AD A1   4F FB 56 61 BD D4 F9 3D  ..)W....O.Va...=
Server write key:
0000: 3E 55 BD 58 C1 06 06 64   A3 9D 59 25 3D D3 37 95  >U.X...d..Y%=.7.
0010: 3D 92 B3 89 AB E9 5E 30   CF 64 D4 B3 2E 85 C5 94  =.....^0.d......
Client write IV:
0000: A4 BE 2B 6D                                        ..+m
Server write IV:
0000: 4C 84 3A 66                                        L.:f
update handshake state: change_cipher_spec
upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data:  { 51, 47, 253, 85, 108, 121, 204, 99, 206, 252, 63, 253 }
***
update handshake state: finished[20]
upcoming handshake states: server change_cipher_spec[-1]
upcoming handshake states: server finished[20]
main, WRITE: TLSv1.2 Handshake, length = 40
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1.2 ALERT:  fatal, unexpected_message
%% Invalidated:  [Session-1, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLException: Received fatal alert: unexpected_message
Exception in thread "main" org.postgresql.util.PSQLException: SSL error: Received fatal alert: unexpected_message

Thanks.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 40 (25 by maintainers)

Commits related to this issue

Most upvoted comments

I think I found the problem, after stepping through the SSL code… the private key was not loading from an “invalid format” exception. My key was in PEM format. I converted it to DER format with

openssl pkcs8 -topk8 -inform PEM -in my.key -outform DER -nocrypt -out my.key.der

and then re-configured the connection to use the my.key.der version… and then it works!

@changetoblow I think I know what your problem was. The documentation of the JDBC driver suggests the following command for the conversion: openssl pkcs8 -topk8 -inform PEM -in my.key -outform DER -out my.key.der The above suggested command is: openssl pkcs8 -topk8 -inform PEM -in my.key -outform DER -nocrypt -out my.key.der

The former lacks the “nocrypt” option. Apparently, even without specifying a password during conversion, if you use the first command, the connection will fail. (Except if you specify the “sslpassword” option with an empty string value perhaps? I haven’t tested that.)

I’ll submit a PR on docs this weekend to save everyone headaches.

I’ll see if I can submit a PR on supporting PEM format.

@Mrk-Nguyen pull requests are welcome. I’m ambivalent, although if either of you wanted to fix the docs so that nobody else wastes this much time I’d be grateful

@msqr Confirmed it now works! Thanks for looking into this.

@davecramer should the JDBC driver support PEM format as well? psql accepts the key in PEM format, so I don’t see why the JDBC driver should not accept PEM as well.

Yes, I’ve seen this before, now I have to recall how I fixed it.