web-dev-qa-db-ja.com

SSLワイルドカードサーバー証明書を使用し、クライアント証明書を使用しないでPostgreSQL 9.6に接続しようとするとエラーが発生する

2012年1月以降、2048ビットのSSLワイルドカードサーバー証明書とパスワードベースの(クライアント証明書なし)リモート接続で構成されたAmazon LinuxのPostgreSQL 9.6.11データベースがあります。最近の証明書のアップグレード後(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 を介して認証パスを確認したところ、2つのパスが利用可能であることがわかりました( パス#1 および パス#2 ):

SSLTest Certificate Paths

PostgreSQL 9.6 SSLによる安全なTCP/IP接続 のドキュメントから:

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の個々の証明書のフィンガープリントをチェックして、IDを確認しました。

# 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の個々の証明書のフィンガープリントをチェックして、IDを確認しました。

# 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

SSL経由でlocalhostからsslmoderequireおよびverify-full(FQDNを使用)。

# 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アラート「unknown 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

ただし、そのパラメーターにnon-existentファイルを指定すると、(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

仕様 から:

unknown_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 alert 、 "certificate unknown"を生成していますが、有効なプロトコルの1つではありません。

# tail pg_log/postgresql-Wed.log
LOG:  could not accept SSL connection: sslv3 alert certificate unknown

仕様 から:

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の検証に問題があることを示しているようです。

注:この回答 で提供されているスクリプトを使用して新しい証明書を生成し、上記のテストを再度実行しましたが、結果は同じです。

1
vallismortis

Windowsでは、デフォルトのroot.crtroot.crl%APPDATA%\postgresqlに保存されます( このスレッド は、正しい方向を示しています)。

これらのファイルを削除すると、sslパラメーターを使用せずにpsqlを介してリモートサーバーに正常に接続できました(デフォルトではsslmode=requireでsslを自動ネゴシエートします):

C:\>"Program Files\PostgreSQL\9.6\bin\psql.exe" "postgresql://mydbuser@[REDACTED].org:5432/mydb"
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

予想通り、sslmode=verify-caまたはsslmode=verify-fullを強制しようとすると、psqlは接続に失敗します。

C:\>"Program Files\PostgreSQL\9.6\bin\psql.exe" "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=verify-ca"
psql: root certificate file "C:\Users\[USERNAME]\AppData\Roaming/postgresql/root.crt" does not exist
Either provide the file or change sslmode to disable server certificate verification.

C:\>"Program Files\PostgreSQL\9.6\bin\psql.exe" "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=verify-full"
psql: root certificate file "C:\Users\[USERNAME]\AppData\Roaming/postgresql/root.crt" does not exist
Either provide the file or change sslmode to disable server certificate verification.

さらに、JDBC経由で接続しようとすると、同じエラーが発生します(JDBCのデフォルトはsslmode=verify-fullであるため)。

org.postgresql.util.PSQLException: Could not open SSL root certificate file C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crt.
    at org.postgresql.ssl.LibPQFactory.<init>(LibPQFactory.Java:120)
    at org.postgresql.core.SocketFactoryFactory.getSslSocketFactory(SocketFactoryFactory.Java:61)
    at org.postgresql.ssl.MakeSSL.convert(MakeSSL.Java:33)
    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 Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.Java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.Java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.Java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.Java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.Java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.Java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.Java:363)
    at org.Eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.Java:89)
    at org.Eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.Java:41)
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.Java:541)
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.Java:763)
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.Java:463)
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.Java:209)
Caused by: Java.io.FileNotFoundException: C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crt (The system cannot find the file specified)
    at Java.io.FileInputStream.open0(Native Method)
    at Java.io.FileInputStream.open(FileInputStream.Java:195)
    at Java.io.FileInputStream.<init>(FileInputStream.Java:138)
    at Java.io.FileInputStream.<init>(FileInputStream.Java:93)
    at org.postgresql.ssl.LibPQFactory.<init>(LibPQFactory.Java:117)
    ... 38 more

トップレベルのルート証明書のみ(またはパス#1またはパス#2の両方のトップレベルのルート証明書)をC:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crtに配置すると、Javaで正常に接続できます=(verify-full!のワイルドカード証明書を使用しても問題ありません):

Connecting with URL: jdbc:postgresql://[REDACTED].org:5432/mydb
PostgreSQL JDBC Driver 42.2.5   
Trying to establish a protocol version 3 connection to [REDACTED].org:5432
converting regular socket connection to ssl
Canonical Host name for [REDACTED].org is [REDACTED].org
Server name validation pass for [REDACTED].org, subjectAltName *.[REDACTED].org

同様に、Linux psqlクライアントで同じことを行うと、

# cat certs/path_1/3_root_usertrust-selfsigned.crt > ~/.postgresql/root.crt
# 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.
n4l_live=> \q

健全性チェックとして、パス#1の証明書のみがserver.crtにあるが、パス#2のルートでverify-fullを試行する場合:

# cat certs/path_2/4_root_addtrustroot-selfsigned.crt > .postgresql/root.crt
# psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=verify-full"
psql: SSL error: certificate verify failed

次に、パス#1のルート証明書も追加します。

# cat certs/path_1/3_root_usertrust-selfsigned.crt >> .postgresql/root.crt
# 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.
n4l_live=> \q

私の質問における私の誤解は次のとおりでした:

  • Windows上のPostgreSQLはWindows証明書ストアを使用します(FALSE!
  • JDBC PostgreSQLドライバーはデフォルトのJava keystore(FALSE!)を使用します)

これは、C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crtが存在する場合、そのファイルがどこにあるかを示すメッセージがないという事実によって補強されました(私のレーダーでさえ、そのフォルダーを調べていませんでした)。

追加のCRLを追加するには、ダウンロードして DERからPEMに変換 する必要があります。

wget http://crl.usertrust.com/AddTrustExternalCARoot.crl
openssl crl -inform DER -in AddTrustExternalCARoot.crl -outform PEM -out AddTrustExternalCARoot_CRL.pem

wget http://crl.usertrust.com/USERTrustRSACertificationAuthority.crl
openssl crl -inform DER -in USERTrustRSACertificationAuthority.crl -outform PEM -out USERTrustRSACertificationAuthority_CRL.pem

cat USERTrustRSACertificationAuthority_CRL.pem AddTrustExternalCARoot_CRL.pem > root.crl

しかし、このroot.crl(中間証明書のCRL)を~/.postgresqlにコピーすると、クライアント接続が、最初と同じエラーで失敗することがわかりました。

# cp ../data/root.crl ~/.postgresql
# psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=verify-full"
psql: SSL error: certificate verify failed

最終的にリモート接続でCRLを機能させることができなかったため、構成を簡略化するためにクライアントのroot.crlを削除しました。これで、psqlとJavaの両方からverify-full接続が成功しました。 CRLの詳細については、この 関連する質問 を参照してください。

私が学んだこと:

  • PostgreSQL JDBCドライバーはC:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crtの中間証明書を必要とせず、verify-fullのパス#1またはパス#2のルート証明書を受け入れます。
  • C:\Users\[USERNAME]\AppData\Roaming\postgresql\root.crl(Linuxでは~/.postgresql/root.crl)がなくても問題ありません
  • root.crlがクライアントに存在する場合は、サーバーが許可する各検証パスの正しいCRLをすべて含める必要があります
  • root.crlが提供されているが、1つ以上のルートCAにCRL配布ポイントが関連付けられていない場合、certificate verify failedメッセージで接続が失敗することがあります。

(私の場合)ルートCA証明書にはCRLが関連付けられておらず、 OpenSSLバグ をトリガーしている可能性があることがわかりました。

# psql "postgresql://mydbuser@[REDACTED].org:5432/mydb?ssl=true&sslmode=verify-full"
psql: SSL error: certificate verify failed

このバグを確認する同等のopensslコマンドは次のとおりです。

# openssl verify -crl_check -CAfile root.crt -CRLfile root.crl server.crt
server.crt: OU = Domain Control Validated, OU = PositiveSSL Wildcard, CN = *.[REDACTED].org
error 3 at 0 depth lookup:unable to get certificate CRL

上記のバグが本当にこのエラーの原因である場合、以前のComodo証明書でroot.crlを使用できたのは、ルートCA証明書にCRL配布ポイントがあったため、このバグがトリガーされなかったためです。短期的には、私の回避策はroot.crlを削除するだけで、接続が機能するようになります。

1
vallismortis

中間証明書はserver.crtにある必要があります。root.crtに追加する必要があるかどうかはわかりません。 PostgreSQLのドキュメントを参照してください

編集:

完全検証でSSLをセットアップするために必要なすべてを生成するスクリプトを作成しました。実行して、動作するか確認してください。

#!/bin/bash

rm -rf /tmp/pg-ssl
mkdir -p /tmp/pg-ssl

openssl req -new -nodes -text -out root.csr -keyout root.key -subj "/CN=root.yourdomain.com"
chmod og-rwx root.key
openssl x509 -req -in root.csr -text -days 3650 -extfile /etc/ssl/openssl.cnf -extensions v3_ca -signkey root.key -out root.crt


openssl req -new -nodes -text -out intermediate.csr -keyout intermediate.key -subj "/CN=intermediate.yourdomain.com"
chmod og-rwx intermediate.key
openssl x509 -req -in intermediate.csr -text -days 1825 -extfile /etc/ssl/openssl.cnf -extensions v3_ca -CA root.crt -CAkey root.key -CAcreateserial -out intermediate.crt


openssl req -new -nodes -text -out server.csr -keyout server.key -subj "/CN=dbhost.yourdomain.com"
chmod og-rwx server.key
openssl x509 -req -in server.csr -text -days 365 -CA intermediate.crt -CAkey intermediate.key -CAcreateserial -out server.crt

cat server.crt intermediate.crt > bundle.crt 


echo "ssl = true"
echo "ssl_cert_file = '/tmp/pg-ssl/bundle.crt'"
echo "ssl_key_file = '/tmp/pg-ssl/server.key'"

echo "add server ip in hosts file <IP> dbhost.yourdomain.com"
echo "copy root.crt to client"
echo 'connect with psql "postgresql://[email protected]:5432/dev?ssl=true&sslmode=verify-full&sslrootcert=/tmp/pg-ssl/root.crt"'

必ずサーバーを再起動し、psqlがサーバーのIDを確認できるクライアントにroot.crtをコピーしてください。テストの目的で、クライアントの/etc/hostsファイルを変更して、クライアントの観点からCNを有効にする必要があります。

1
vx3r