我在 Amazon Linux 上有一个 PostgreSQL 9.6.11 数据库,自 2012 年 1 月以来,该数据库已配置了 2048 位 SSL 通配符服务器证书和基于密码(无客户端证书)的远程连接。在最近的证书升级后(Comodo,现在是 Sectigo) ,我无法再通过 SSL 与该数据库建立远程 psql 或 JDBC 连接。
我的目标是能够通过 psql 和 JDBC 远程连接到这个 PostgreSQL 数据库。
从服务器密钥(自从我有远程访问工作以来它没有改变)开始,我试图涵盖整个系列的步骤,以验证我的密钥、证书、防火墙和数据库设置是否正确。
我一定错过了什么,因为我无法通过 psql 或 JDBC 远程连接。
我错过了什么可能导致这些远程连接失败?
故障排除步骤
作为用户postgres:
# cd /var/lib/pgsql96/data
postgresql.conf
我曾尝试限制密码集以尝试强制所有 SSL 连接使用 TLSv1.2。由于客户端行为没有区别,我注释掉了 ssl_ciphers 和 ssl_prefer_server_ciphers 以允许使用默认值。
ssl = on
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL:!SSLv2:!SSLv3:!TLSv1:!TLSv1.1'
#ssl_prefer_server_ciphers = on
ssl_cert_file = 'server.crt' # wildcard cert plus intermediate certs
ssl_key_file = 'server.key' # private key
#ssl_ca_file = 'root.crt' # commented out - do not require client certs
#ssl_crl_file = 'root.crl' # commented out - no client certificates
pg_hba.conf
该文件设置为仅允许本地主机的公共 IP 地址和我正在测试的远程主机。我不想要求客户端证书,只需要使用所需密码进行加密。
hostssl all all 11.222.11.222/32 password # localhost
hostssl all all 34.84.31.82/32 password # remote host
我通过ssltest检查了认证路径,发现有两个可用路径(路径 #1和路径 #2):
从PostgreSQL 9.6 Secure TCP/IP Connections with SSL的文档中:
server.crt 中的第一个证书必须是服务器的证书,因为它必须与服务器的私钥匹配。“中间”证书颁发机构的证书也可以附加到文件中。这样做可以避免在客户端上存储中间证书的必要性,假设根证书和中间证书是使用 v3_ca 扩展创建的。这使得中间证书更容易过期。
无需将根证书添加到 server.crt。相反,客户端必须拥有服务器证书链的根证书。
组装和验证路径 #1 的证书链
# ls -l
-rw------- 1 postgres postgres 2313 Aug 15 00:26 1_wildcard_server.crt
-rw------- 1 postgres postgres 2167 Aug 15 00:27 2_intermediate_sectigo.crt
-rw------- 1 postgres postgres 2094 Aug 15 00:27 3_root_usertrust-selfsigned.crt
我检查了路径 #1 中每个证书的指纹以确认其身份:
# openssl x509 -in 1_wildcard_server.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=8C:69:06:8E:81:31:30:6E:DA:DD:C2:1C:38:83:73:67:97:3D:DB:37:78:B8:49:D7:7E:32:A8:3F:1F:8B:08:AB
# openssl x509 -in 2_intermediate_sectigo.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=7F:A4:FF:68:EC:04:A9:9D:75:28:D5:08:5F:94:90:7F:4D:1D:D1:C5:38:1B:AC:DC:83:2E:D5:C9:60:21:46:76
# openssl x509 -in 3_root_usertrust-selfsigned.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=E7:93:C9:B0:2F:D8:AA:13:E2:1C:31:22:8A:CC:B0:81:19:64:3B:74:9C:89:89:64:B1:74:6D:46:C3:D4:CB:D2
并且还检查了证书的文本版本,以确认中间证书和根证书具有 v3_ca 扩展名(通配符服务器证书没有此扩展名):
# openssl x509 -in 1_wildcard_server.crt -text
...
X509v3 Basic Constraints: critical
CA:FALSE
...
# openssl x509 -in 2_intermediate_sectigo.crt -text
...
X509v3 Basic Constraints: critical
CA:TRUE
...
# openssl x509 -in 3_root_usertrust-selfsigned.crt -text
...
X509v3 Basic Constraints: critical
CA:TRUE
...
使用 OpenSSL 验证路径 #1 的证书链:
# openssl verify -verbose -CAfile 3_root_usertrust-selfsigned.crt -untrusted 2_intermediate_sectigo.crt 1_wildcard_server.crt
1_wildcard_server.crt: OK
为路径 #1 创建捆绑的服务器和中间证书:
# cat 1_wildcard_server.crt > server.crt
# cat 2_intermediate_sectigo.crt >> server.crt
为路径 #1 创建捆绑的中间证书和根证书(具有 v3_ca 扩展名的证书)(尽管仅在需要客户端证书时才需要):
# cat 2_intermediate_sectigo.crt > root.crt
# cat 3_root_usertrust-selfsigned.crt >> root.crt
组装和验证路径 #2 的证书链
# ls -l
-rw------- 1 postgres postgres 2313 Aug 15 00:26 1_wildcard_server.crt
-rw------- 1 postgres postgres 2167 Aug 15 00:27 2_intermediate_sectigo.crt
-rw------- 1 postgres postgres 1956 Aug 15 00:35 3_intermediate_usertrust.crt
-rw------- 1 postgres postgres 1521 Aug 15 00:27 4_root_addtrustroot-selfsigned.crt
我检查了路径 #2 中每个证书的指纹以确认它们的身份:
# openssl x509 -in 1_wildcard_server.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=8C:69:06:8E:81:31:30:6E:DA:DD:C2:1C:38:83:73:67:97:3D:DB:37:78:B8:49:D7:7E:32:A8:3F:1F:8B:08:AB
# openssl x509 -in 2_intermediate_sectigo.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=7F:A4:FF:68:EC:04:A9:9D:75:28:D5:08:5F:94:90:7F:4D:1D:D1:C5:38:1B:AC:DC:83:2E:D5:C9:60:21:46:76
# openssl x509 -in 3_intermediate_usertrust.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=1A:51:74:98:0A:29:4A:52:8A:11:07:26:D5:85:56:50:26:6C:48:D9:88:3B:EA:69:2B:67:B6:D7:26:DA:98:C5
# openssl x509 -in 4_root_addtrustroot-selfsigned.crt -noout -sha256 -fingerprint
SHA256 Fingerprint=68:7F:A4:51:38:22:78:FF:F0:C8:B1:1F:8D:43:D5:76:67:1C:6E:B2:BC:EA:B4:13:FB:83:D9:65:D0:6D:2F:F2
并且还检查了证书的文本版本,以确认中间证书和根证书具有 v3_ca 扩展名(通配符服务器证书没有此扩展名):
# openssl x509 -in 1_wildcard_server.crt -text
...
X509v3 Basic Constraints: critical
CA:FALSE
...
# openssl x509 -in 2_intermediate_sectigo.crt -text
...
X509v3 Basic Constraints: critical
CA:TRUE
...
# openssl x509 -in 3_intermediate_usertrust.crt -text
...
X509v3 Basic Constraints: critical
CA:TRUE
...
# openssl x509 -in 4_root_addtrustroot-selfsigned.crt -text
...
X509v3 Basic Constraints: critical
CA:TRUE
...
使用 OpenSSL 验证路径 #2 的证书链(来自本文):
# openssl verify -verbose -CAfile 4_root_addtrustroot-selfsigned.crt -untrusted 3_intermediate_usertrust.crt 2_intermediate_sectigo.crt
2_intermediate_sectigo.crt: OK
为路径 #2 创建捆绑的服务器和中间证书:
# cat 1_wildcard_server.crt > server.crt
# cat 2_intermediate_sectigo.crt >> server.crt
# cat 3_intermediate_usertrust.crt >> server.crt
为路径 #2 创建捆绑的中间证书和根证书(具有 v3_ca 扩展名的证书)(尽管仅在需要客户端证书时才需要):
# cat 2_intermediate_sectigo.crt > root.crt
# cat 3_intermediate_usertrust.crt >> root.crt
# cat 4_root_addtrustroot-selfsigned.crt >> root.crt
已验证的密钥和证书权限(如在此问题中)。
# ls -l *.key *.crt *.crl-bash-4.2$ ls -l *.key *.crt *.crl
-rw-r----- 1 postgres postgres 963 Aug 14 21:12 root.crl
-rw-r--r-- 1 postgres postgres 1521 Aug 15 01:27 root.crt
-rw-r--r-- 1 postgres postgres 6436 Aug 15 01:27 server.crt
-rw------- 1 postgres postgres 1679 May 28 19:33 server.key
按照 Comodo的说明确认服务器密钥正常。
# openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017
# openssl rsa -check -noout -in server.key
RSA key ok
确认证书和私钥模数相同。
# openssl rsa -modulus -noout -in server.key
Modulus=[REDACTED]
# openssl x509 -modulus -noout -in server.crt
Modulus=[REDACTED]
测试了 CRL 并验证了颁发者:
# openssl crl -in root.crl -text
Certificate Revocation List (CRL):
Version 2 (0x1)
Signature Algorithm: sha1WithRSAEncryption
Issuer: /C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
Last Update: May 28 00:12:38 2019 GMT
Next Update: Jun 1 00:12:38 2019 GMT
CRL extensions:
X509v3 Authority Key Identifier:
keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
X509v3 CRL Number:
5275
Revoked Certificates:
Serial Number: 537B76564F297F14DC6943E922AD2C79
Revocation Date: Dec 14 15:58:30 2015 GMT
Serial Number: 46EAF096054CC5E3FA65EA6E9F42C664
Revocation Date: Dec 14 15:58:30 2015 GMT
Serial Number: 3ACDAB9C759886BCAF74E5DF81A9F4E8
Revocation Date: Dec 14 15:58:30 2015 GMT
Serial Number: 79174AA9141736FE15A7CA9F2CFF4588
Revocation Date: Apr 30 20:03:54 2018 GMT
Serial Number: 74C18753F7EEB4EA238D8416B5AC7646
Revocation Date: Oct 9 09:11:57 2018 GMT
Signature Algorithm: sha1WithRSAEncryption
38:3a:7d:3e:ee:be:48:e7:93:c3:91:0a:c3:47:46:11:87:83:
[TRIMMED]
5f:16:1a:38
-----BEGIN X509 CRL-----
MIICnTCCAYUCAQEwDQYJKoZIhvcNAQEFBQAwbzELMAkGA1UEBhMCU0UxFDASBgNV
[TRIMMED]
iEx7Li7fLtVPxbIU4aqaKU+15QEE37eJWRccBnuhqJqEDM+ML+k67Hj1yeLaXxYa
OA==
-----END X509 CRL-----
启动 PostgreSQL 服务。
# service postgresql96 start
Starting postgresql96 service: [ OK ]
验证日志文件中没有错误。
# cat ../pgstartup.log
LOG: redirecting log output to logging collector process
HINT: Future log output will appear in directory "pg_log".
# cat pg_log/postgresql-Wed.log
LOG: database system was shut down at 2019-08-14 15:01:03 UTC
LOG: MultiXact member wraparound protections are now enabled
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
使用sslmode require和verify-full(使用 FQDN)从localhost通过 SSL 成功连接到数据库。
# psql "postgresql://mydbuser@localhost:5432/mydb?ssl=true&sslmode=require"
psql (9.6.11)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
mydb=> \q
# psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=verify-full"
Password: ********
psql (9.6.11)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
mydb=> \q
结合本法、本法、本法、本法,通过Java远程检索并直观验证SSL证书链。这确认端口 5432 在防火墙上为此主机打开,并且证书和链可通过 JDBC 检索。
Supported Protocols: 5
SSLv2Hello
SSLv3
TLSv1
TLSv1.1
TLSv1.2
Enabled Protocols: 3
TLSv1
TLSv1.1
TLSv1.2
Enabled Cipher suites: 43
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_RSA_WITH_AES_256_CBC_SHA
TLS_DHE_DSS_WITH_AES_256_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
TLS_EMPTY_RENEGOTIATION_INFO_SCSV
Cert 1 (active):
Thumbprint SHA1 : B5:2D:43:A8:0F:C6:C3:39:1F:2D:BB:9C:30:A5:4B:8D:DF:5F:9B:F8
Fingerprint SHA256: 8c69068e8131306edaddc21c38837367973ddb3778b849d77e32a83f1f8b08ab
Subject: CN=*.[REDACTED].org,OU=PositiveSSL Wildcard,OU=Domain Control Validated
Issuer: CN=Sectigo RSA Domain Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
Cert 2 (active):
Thumbprint SHA1 : 33:E4:E8:08:07:20:4C:2B:61:82:A3:A1:4B:59:1A:CD:25:B5:F0:DB
Fingerprint SHA256: 7fa4ff68ec04a99d7528d5085f94907f4d1dd1c5381bacdc832ed5c960214676
Subject: CN=Sectigo RSA Domain Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
Issuer: CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
Cert 3 (active):
Thumbprint SHA1 : 33:E4:E8:08:07:20:4C:2B:61:82:A3:A1:4B:59:1A:CD:25:B5:F0:DB
Fingerprint SHA256: 7fa4ff68ec04a99d7528d5085f94907f4d1dd1c5381bacdc832ed5c960214676
Subject: CN=Sectigo RSA Domain Validation Secure Server CA,O=Sectigo Limited,L=Salford,ST=Greater Manchester,C=GB
Issuer: CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
Cert 4 (active):
Thumbprint SHA1 : EA:B0:40:68:9A:0D:80:5B:5D:6F:D6:54:FC:16:8C:FF:00:B7:8B:E3
Fingerprint SHA256: 1a5174980a294a528a110726d5855650266c48d9883bea692b67b6d726da98c5
Subject: CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US
Issuer: CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE
我确认上述根证书(AddTrust External CA Root)都在此处推荐的默认 Java 密钥库中(并且还确认默认情况下它们也在 Windows 密钥库中):
C:\Windows\System32>"C:\Program Files\Java\jdk1.8.0_212\jre\bin\keytool.exe" -keystore "C:\Program Files\Java\jdk1.8.0_212\jre\lib\security\cacerts" -storepass
changeit -list
Keystore type: jks
Keystore provider: SUN
Your keystore contains 95 entries
....
usertrustrsaca [jdk], Aug 25, 2016, trustedCertEntry,
Certificate fingerprint (SHA1): 2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E
....
addtrustexternalca [jdk], Aug 25, 2016, trustedCertEntry,
Certificate fingerprint (SHA1): 02:FA:F3:E2:91:43:54:68:60:78:57:69:4D:F5:E4:5B:68:85:18:68
....
尝试从远程主机通过 openssl 连接到 PostgreSQL(如在这个问题中)。
# openssl version
OpenSSL 1.1.0h 27 Mar 2018
# openssl s_client -connect [REDACTED].org:5432 -state -msg -showcerts -debug
CONNECTED(00000003)
SSL_connect:before/connect initialization
write to 0x2070760 [0x20fe520] (289 bytes => 289 (0x121))
0000 - 16 03 01 01 1c 01 00 01-18 03 03 0c 53 44 0c a3 ............SD..
[TRIMMED]
0110 - 03 01 03 02 03 03 02 01-02 02 02 03 00 0f 00 01 ................
0120 - 01 .
>>> TLS 1.2 [length 0005]
16 03 01 01 1c
>>> TLS 1.2 Handshake [length 011c], ClientHello
01 00 01 18 03 03 0c 53 44 0c a3 e2 21 36 f2 b0
[TRIMMED]
01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03
03 02 01 02 02 02 03 00 0f 00 01 01
SSL_connect:SSLv2/v3 write client hello A
read from 0x2070760 [0x2103a80] (7 bytes => 0 (0x0))
139650021263184:error:140790E5:SSL routines:ssl23_write:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 289 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Key-Arg : None
Krb5 Principal: None
PSK identity: None
PSK identity hint: None
Start Time: 1565797370
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
# tail pg_log/postgresql-Wed.log
LOG: invalid length of startup packet
尝试通过 Windows psql 客户端 (9.6.5) 远程连接。
C:\Program Files\PostgreSQL\9.6\bin>psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=require"
psql: SSL error: certificate verify failed
出于某种原因,psql 客户端正在发送一个 TLSv1 警报,“未知 ca”:
# tail pg_log/postgresql-Wed.log
LOG: could not accept SSL connection: tlsv1 [alert][15] unknown ca
此外,如果我指定根证书:
C:\Program Files\PostgreSQL\9.6\bin>psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=require&sslrootcert=root.crt"
psql: SSL error: certificate verify failed
或者如果我将该参数留空:
C:\Program Files\PostgreSQL\9.6\bin>psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=require&sslrootcert="
psql: SSL error: certificate verify failed
但是,如果我为该参数指定一个不存在的文件,我能够成功连接(使用 sslmode=require):
C:\Program Files\PostgreSQL\9.6\bin>psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=require&sslrootcert=x"
Password:
psql (9.6.5, server 9.6.11)
WARNING: Console code page (437) differs from Windows code page (1252)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
n4l_live=> \q
从规范:
未知的_ca
A valid certificate chain or partial chain was received, but the certificate was not accepted because the CA certificate could not be located or couldn't be matched with a known, trusted CA. This message is always fatal.
尝试通过 Java 客户端与postgresql-42.2.5.jar 连接。
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
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)
... 36 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:262)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:330)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:237)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621)
... 44 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 50 more
出于某种原因,Java 客户端正在生成 SSLv3警报,“证书未知”,即使它不是启用的协议之一:
# tail pg_log/postgresql-Wed.log
LOG: could not accept SSL connection: sslv3 alert certificate unknown
从规范:
证书_未知
Some other (unspecified) issue arose in processing the certificate, rendering it unacceptable.
PostgreSQL JDBC 驱动程序文档表明Java 客户端(默认情况下)将尝试使用verify-full
服务器证书,这可能是此处产生的错误与使用 psql 客户端不同的原因:
最简单的是 ssl=true,将其传递给驱动程序将导致驱动程序验证 SSL 证书并验证主机名(与 相同
verify-full
)。请注意,这与默认为非验证 SSL 连接的 libpq 不同。
上述失败(以及使用 指定不存在的根证书时的成功sslmode=require
)似乎表明验证证书的 CA 存在一些问题。
注意:我使用此答案中提供的脚本生成新证书并再次执行上述测试,结果相同。
中间证书必须在
server.crt
我不确定是否需要将其添加到root.crt
. 请参考PostgreSQL 文档编辑:
我刚刚创建了一个脚本来生成您设置 SSL 所需的所有内容并进行完整验证。你能运行它并确认它是否有效吗?
确保重新启动服务器并复制
root.crt
到psql
可以验证服务器身份的客户端。出于测试目的/etc/hosts
,必须修改客户端上的文件以使 CN 从客户端角度有效。在 Windows 上,默认值
root.crt
和root.crl
存储在%APPDATA%\postgresql
(这个线程为我指出了正确的方向)。当我删除这些文件时,我能够通过 psql 成功连接到远程服务器,而无需使用任何 ssl 参数(默认为自动协商 ssl
sslmode=require
):正如预期的那样,当我尝试强制
sslmode=verify-ca
orsslmode=verify-full
时,psql 无法连接:此外,当我尝试通过 JDBC 连接时,我得到了同样的错误(因为 JDBC 默认为
sslmode=verify-full
):当我仅将顶级根证书(或路径 #1 或路径 #2 的顶级根证书)放入时
C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crt
,我能够成功连接 Java(使用通配符证书没有问题verify-full
!):同样,当我在我的 Linux psql 客户端上执行相同操作时:
作为健全性检查,如果只有路径 #1 的证书在 server.crt 中,但我尝试
verify-full
使用路径 #2 的根目录:然后我还附加了路径 #1 的根证书:
我对问题的误解是:
This was reinforced by the fact that if
C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crt
exists, there are no messages indicating where that file is (it was not even on my radar to look in that folder).In order to add the additional CRLs, I needed to download and convert from DER to PEM:
But then I found that if I copy this
root.crl
(CRL for intermediate certificates) into~/.postgresql
, my client connections fail with the same error that I started with:I was ultimately unable to get CRL working for remote connections, so I deleted
root.crl
on the clients to simplify the configuration. I now have successfulverify-full
connections from both psql and Java. For a detailed write-up on CRLs, see this related question.What I've learned:
C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crt
, and will accept the root certificate of either Path #1 or Path #2 forverify-full
.C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crl
(or~/.postgresql/root.crl
on Linux) is OKroot.crl
is present on the client, it must contain all of the correct CRLs for each validation path allowed by the server.root.crl
is provided, but one or more root CA does not have an associated CRL Distribution Point, the connections may fail with acertificate verify failed
message.I found that (in my case) neither of the root CA certificates had a CRL associated with it, which may be triggering an OpenSSL bug:
The equivalent openssl command that confirms this bug is:
如果上面的错误确实是造成这个错误的原因,那么我能够使用
root.crl
我以前的 Comodo 证书的原因是根 CA 证书有一个 CRL 分发点,所以这个错误从未被触发。在短期内,我的解决方法是简单地删除root.crl
,这会导致工作连接。