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
- Create self-signed certificates for both server and client using openssl.
- Install server certificates, key, and client certificate into CA authority certificate.
- Configure
pg_hba.conffor hostssl:
hostssl all all 127.0.0.1/32 md5
hostssl all all ::1/128 md5
- Setup a database, a table, and a user account with permissions to the table in the PostgreSQL.
- 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"
- 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();
}
}
javax.net.ssl.SSLException: Received fatal alert: unexpected_messagegets 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
- docs: Add DEM format details for `sslkey` (#1376) Add DEM format details and instructions on how to convert from PEM to DER format as discussed in issue #1364 — committed to pgjdbc/pgjdbc by Mrk-Nguyen 6 years ago
- docs: Add DEM format details for `sslkey` (#1376) Add DEM format details and instructions on how to convert from PEM to DER format as discussed in issue #1364 — committed to davecramer/pgjdbc by Mrk-Nguyen 6 years ago
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
and then re-configured the connection to use the
my.key.derversion… 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.derThe above suggested command is:openssl pkcs8 -topk8 -inform PEM -in my.key -outform DER -nocrypt -out my.key.derThe 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?
psqlaccepts 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.