LinuxでHTTPS通信時にデフォルトで使用される証明書ファイルの場所
はじめに
HTTPS通信をする際、サーバ側からはサーバ証明書(+中間証明書)を提示し、クライアント側では予めインストールされた信頼済みルート証明書を使って、サーバから提示されたサーバ証明書を検証する(※)。
※片方向TLSの場合。双方向の場合はクライアント側からもクライアント証明書を提示する。
サーバ側で使うサーバ証明書はWebサーバとなるソフトウェアの設定ファイル等で指定されるが、クライアント側のルート証明書は何が使われるのだろうか。Windows/MacでWebブラウザがクライアントとなる場合の情報はどこにでもあるが、Linuxでクライアントが非ブラウザの場合の情報があまりなさそうだったので、調べた範囲でまとめてみる。ブラウザだったら証明書を無視して表示することもできるが、独自アプリケーションやスクリプトでLinux間REST通信をする際にHTTPSを使っていたりすると、証明書の検証エラーは通信断を意味するので・・・。
(参考までに)サーバ側
Webサーバとなるソフトウェアの設定ファイルで、使用するサーバ証明書(+中間証明書)を指定する。
Apache + OpenSSL (mod_ssl) の場合
Apacheの設定ファイル(CentOS系の場合、/etc/httpd/conf.d/ssl.conf)でサーバ証明書と秘密鍵の場所を定義
# less /etc/httpd/conf.d/ssl.conf ... SSLCertificateFile /etc/pki/tls/certs/server.crt SSLCertificateKeyFile /etc/pki/tls/private/server.key ...
Tomcatの場合
Tomcatサーバの設定ファイル(server.xml)内のSSL設定部分でサーバ証明書と秘密鍵が含まれるキーストアファイルを指定
# less /opt/apache-tomcat-9.0.34/conf/server.xml ... <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" SSLEnabled="true"> <SSLHostConfig> <Certificate certificateKeystoreFile="conf/keystore.jks" certificationKeystorePassword="changeit" certificationKeyAlias="tomcat" type="RSA" /> </SSLHostConfig> </Connector> ...
クライアント側
クライアントとなるアプリケーションによって異なる。 アプリケーション内で設定しない場合や実行時オプション指定で明に指定しない場合は、デフォルト値が使用される。デフォルト値はアプリケーションと環境(ディストリビューションとそのバージョン、クライアントアプリケーションのビルド時の設定等)によって異なる。
以下、クライアントとしてcurl, openssl, Javaアプリケーションの場合について記載する。
curlの場合
以下、①②の設定値があり、①→②の順に参照する。
①CAfile:使用するCA証明書ファイル(PEM形式)。複数の証明書が含まれてもよい。--cacertオプションで指定可能。
②CApath:CA証明書が格納されたディレクトリ。CA証明書(もしくは証明書へのシンボリックリンク)は{hash}.0という名前になっている必要がある。
{hash}は証明書のsubjectをハッシュ化した値。openssl x509 -in <証明書ファイル> -hash -noout
で確認できる。--capathオプションで指定可能。
①②について、オプションで指定しなかった場合、デフォルト値が使用される。
デフォルト値はどこで決まるか
デフォルト値はビルド時のオプションで指定可能。
例えば下記のようにcurl 7.58.0のソースをダウンロードし、configure --helpで説明が表示される。
# curl -O https://curl.haxx.se/download/curl-7.58.0.tar.gz # tar xvzf curl-7.58.0.tar.gz # cd curl-7.58.0 # ./configure --help ... --with-ca-bundle=FILE Path to a file containing CA certificates (example: /etc/ca-bundle.crt) ... --with-ca-path=DIRECTORY Path to a directory containing CA certificates stored individually, with their filenames in a hash format. This option can be used with OpenSSL, GnuTLS and PolarSSL backends. Refer to OpenSSL c_rehash for details. (example: /etc/certificates) ...
CentOS 7.6.1810で実際にconfigureを実施した結果は以下のようになった。
# ./configure --enable-libcurl-option ... configure: Configured to build curl/libcurl: curl version: 7.58.0 Host setup: x86_64-pc-linux-gnu Install prefix: /usr/local Compiler: gcc SSL support: no (--with-{ssl,gnutls,nss,polarssl,mbedtls,cyassl,axtls,winssl,darwinssl} ) SSH support: no (--with-libssh2) zlib support: no (--with-zlib) brotli support: no (--with-brotli) GSS-API support: no (--with-gssapi) TLS-SRP support: no (--enable-tls-srp) resolver: POSIX threaded IPv6 support: enabled Unix sockets support: enabled IDN support: no (--with-{libidn2,winidn}) Build libcurl: Shared=yes, Static=yes Built-in manual: enabled --libcurl option: enabled (--disable-libcurl-option) Verbose errors: enabled (--disable-verbose) SSPI support: no (--enable-sspi) ca cert bundle: /etc/pki/tls/certs/ca-bundle.crt ca cert path: no ca fallback: no LDAP support: no (--enable-ldap / --with-ldap-lib / --with-lber-lib) LDAPS support: no (--enable-ldaps) RTSP support: enabled RTMP support: no (--with-librtmp) metalink support: no (--with-libmetalink) PSL support: no (libpsl not found) HTTP2 support: disabled (--with-nghttp2) Protocols: DICT FILE FTP GOPHER HTTP IMAP POP3 RTSP SMTP TELNET TFTP
①CAfileは/etc/pki/tls/certs/ca-bundle.crt、②CApathは設定なしとなった。また、ca fallbackについては、①②共にサーバ証明書の検証に失敗した場合に、OS標準の証明書ストアを使用するか、を設定するものと思われるが、これはまだ検証していない。
現在設定されているデフォルト値の確認方法
ではOSに最初から入っているcurlの場合や、yum/aptでインストールした場合はどうしたらいいのだろうか?探した限りでは、あまり標準的なやり方はない模様。
こちらでも議論されており、様々な方法が提案されているが、個人的にはオプション-vをつけてhttpsサーバのURLにアクセスするのが一番シンプルだと思った(実際にping疎通可能なhttpsサーバがいることが前提だが...)。SSLセッション確立が成功しても失敗しても、CAfileとCApathのデフォルト値が表示される。
CentOS 7.6.1810, curl 7.29.0で実行した場合の例: 192.168.12.22(webserver)ではapache + mod_sslでWebサーバが動いており、オレオレ証明書(CN=webserver)が設定されている。
[root@localhost ~]# curl -v https://webserver/ * About to connect() to webserver port 443 (#0) * Trying 192.168.12.22... * Connected to webserver (192.168.12.22) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * Server certificate: * subject: CN=webserver,O=Default Company Ltd,L=Default City,ST=tokyo,C=JP * start date: Apr 29 09:15:04 2020 GMT * expire date: May 29 09:15:04 2020 GMT * common name: webserver * issuer: CN=webserver,O=Default Company Ltd,L=Default City,ST=tokyo,C=JP * NSS error -8172 (SEC_ERROR_UNTRUSTED_ISSUER) * Peer's certificate issuer has been marked as not trusted by the user. * Closing connection 0 curl: (60) Peer's certificate issuer has been marked as not trusted by the user. More details here: http://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none (設定なし)
が確認できる。
実際のデフォルト値はどうなっているか
基本的にはディストリビューションにより異なっている模様。またバージョンによって証明書を配置するディレクトリの構成が異なっている。
CentOS 6, CentOS 7, Ubuntu 18.04にバンドルされているcurlについて調査した。結果を先に記載する。
OS | アプリとバージョン | デフォルトで参照するルート証明書の場所 |
---|---|---|
CentOS 6.10 | curl 7.19.7 | CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none |
CentOS 7.6.1810 | curl 7.29.0 | CAfile: /etc/pki/tls/certs/ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem CApath: none |
Ubuntu 18.04 | curl 7.58.0 | CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs |
※curlはSSL系のライブラリとして、NSSを使用する場合とOpenSSLを使う場合があり、それによっても違うのかとも思ったが、調べた限り上記デフォルト値の考え方自体は変わらないように見えた。使用ライブラリについては、curl -Vでバージョンと共に確認可能。
以下確認の詳細。
CentOS 6
[root@centos6 ~]# cat /etc/redhat-release CentOS release 6.10 (Final) [root@centos6 ~]# curl -V curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2 Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz [root@centos6 ~]# curl -v https://webserver/ * About to connect() to webserver port 443 (#0) * Trying 192.168.12.22... connected * Connected to webserver (192.168.12.22) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * Certificate is signed by an untrusted issuer: 'CN=webserver,O=Default Company Ltd,L=Default City,ST=tokyo,C=JP' * NSS error -8172 * Closing connection #0 * Peer certificate cannot be authenticated with known CA certificates curl: (60) Peer certificate cannot be authenticated with known CA certificates More details here: http://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
であることがわかる。
[root@centos6 ~]# ll /etc/pki/tls/certs total 1164 -rw-r--r--. 1 root root 755417 May 4 01:57 ca-bundle.crt -rw-r--r--. 1 root root 418126 Feb 28 2018 ca-bundle.trust.crt -rwxr-xr-x. 1 root root 610 Aug 14 2019 make-dummy-cert -rw-r--r--. 1 root root 2242 Aug 14 2019 Makefile -rwxr-xr-x. 1 root root 829 Aug 14 2019 renew-dummy-cert
ca-bundle.crtの実体が格納されている。
CentOS 7
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) [root@localhost ~]# curl -V curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.36 zlib/1.2.7 libidn/1.28 libssh2/1.4.3 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-sockets [root@localhost ~]# curl -v https://webserver/ * About to connect() to webserver port 443 (#0) * Trying 192.168.12.22... * Connected to webserver (192.168.12.22) port 443 (#0) * Initializing NSS with certpath: sql:/etc/pki/nssdb * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * Server certificate: * subject: CN=webserver,O=Default Company Ltd,L=Default City,ST=tokyo,C=JP * start date: Apr 29 09:15:04 2020 GMT * expire date: May 29 09:15:04 2020 GMT * common name: webserver * issuer: CN=webserver,O=Default Company Ltd,L=Default City,ST=tokyo,C=JP * NSS error -8172 (SEC_ERROR_UNTRUSTED_ISSUER) * Peer's certificate issuer has been marked as not trusted by the user. * Closing connection 0 curl: (60) Peer's certificate issuer has been marked as not trusted by the user. More details here: http://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option.
CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
であることがわかる。(CentOS 6と同様)
[root@localhost ~]# ll /etc/pki/tls/certs total 16 lrwxrwxrwx. 1 root root 49 May 4 12:51 ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem lrwxrwxrwx. 1 root root 55 Jun 1 2019 ca-bundle.trust.crt -> /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt -rw-------. 1 root root 1468 Apr 11 08:28 localhost.crt -rwxr-xr-x. 1 root root 610 Mar 12 2019 make-dummy-cert -rw-r--r--. 1 root root 2516 Mar 12 2019 Makefile -rwxr-xr-x. 1 root root 829 Mar 12 2019 renew-dummy-cert
ca-bundle.crtがシンボリックリンクになっている。
CentOS7から証明書の管理方針が変わり、update-ca-trustで管理するようになっている。
Ubuntu 18.04
バージョン確認
root@ubuntu-bionic:~# cat /etc/os-release NAME="Ubuntu" VERSION="18.04.3 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.3 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic root@ubuntu-bionic:~# curl -V curl 7.58.0 (x86_64-pc-linux-gnu) libcurl/7.58.0 OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3 Release-Date: 2018-01-24 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL
ライブラリとしてNSSではなくOpenSSLを使っている。
root@ubuntu-bionic:~# curl -v https://webserver/ * Trying 192.168.12.22... * TCP_NODELAY set * Connected to webserver (192.168.12.22) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (OUT), TLS alert, Server hello (2): * SSL certificate problem: self signed certificate * stopped the pause stream! * Closing connection 0 curl: (60) SSL certificate problem: self signed certificate More details here: https://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above.
CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
であることがわかる。
/etc/ssl/certs/を確認する。
root@ubuntu-bionic:~# ll /etc/ssl/certs total 632 drwxr-xr-x 3 root root 16384 May 4 09:12 ./ drwxr-xr-x 4 root root 4096 Dec 18 19:12 ../ lrwxrwxrwx 1 root root 45 May 2 13:35 02265526.0 -> Entrust_Root_Certification_Authority_-_G2.pem lrwxrwxrwx 1 root root 36 May 2 13:35 03179a64.0 -> Staat_der_Nederlanden_EV_Root_CA.pem lrwxrwxrwx 1 root root 27 May 2 13:35 062cdee6.0 -> GlobalSign_Root_CA_-_R3.pem lrwxrwxrwx 1 root root 25 May 2 13:35 064e0aa9.0 -> QuoVadis_Root_CA_2_G3.pem lrwxrwxrwx 1 root root 50 May 2 13:35 06dc52d5.0 -> SSL.com_EV_Root_Certification_Authority_RSA_R2.pem ...以下略。ルート証明書へのシンボリックリンクがずらっと入っている。 root@ubuntu-bionic:~# ll /etc/ssl/certs/ca-certificates.crt -rw-r--r-- 1 root root 207436 May 4 09:12 /etc/ssl/certs/ca-certificates.crt
/etc/ssl/certsには、信頼済みルート証明書へのシンボリックリンクと、信頼済みルート証明書の実体がバンドルされたファイルca-certificates.crtが入っている。
ちなみに、下記の方法(update-ca-certificates)で独自のルート証明書を追加した場合、シンボリックリンクの作成とca-certificates.crtへの追加の両方が実行される。
opensslの場合
openssl s_client -connect ...
でクライアントアプリとして使う場合を想定。
curlと同様、①CAfileと②CApathで設定する。 参照する順番はcurlと同様①→②。 本家のドキュメントにも記載があった。
https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_load_verify_locations.html
When looking up CA certificates, the OpenSSL library will first search the certificates in CAfile, then those in CApath.
デフォルト値はどこで決まるか
先ほどと同じ本家のページに記載があった。
The default CA certificates directory is called "certs" in the default OpenSSL directory.
(中略)
The default CA certificates file is called "cert.pem" in the default OpenSSL directory.
CAfile: "OpenSSL directory"配下のcert.pem
CApath: "OpenSSL directory"配下のcertsディレクトリ
となる。
※こちらのページ によると、cryptlib.hで定義されている模様。
"OpenSSL directory"はビルド時に設定可能。また環境変数での定義も可能。OpenSSLのソースをダウンロードし、フォルダ配下のINSTALLファイルを確認すると記載がある。
下記、OpenSSL 1.1.1で確認:
# less INSTALL ... --openssldir=DIR Directory for OpenSSL configuration files, and also the default certificate and key store. Defaults are: Unix: /usr/local/ssl Windows: C:\Program Files\Common Files\SSL or C:\Program Files (x86)\Common Files\SSL OpenVMS: SYS$COMMON:[OPENSSL-COMMON]
Unixではデフォルトは/usr/local/sslになるようだ。
現在設定されているデフォルト値の確認方法
デフォルトのCAfileとCApathのファイル名/ディレクトリ名は上記の通り固定値のようなので、"OPENSSLDIR"を確認すればよい。
openssl version -a
で確認できるようだ。
実際のデフォルト値はどうなっているか
CentOS 6, CentOS 7, Ubuntu 18.04にバンドルされているopensslについて調査した。先に結果を記載する。
OS | アプリとバージョン | デフォルトで参照するルート証明書の場所 |
---|---|---|
CentOS 6.10 | OpenSSL 1.0.1e-fips | CAfile: /etc/pki/tls/cert.pem -> /etc/pki/tls/certs/ca-bundle.crt CApath: /etc/pki/tls/certs |
CentOS 7.6.1810 | OpenSSL 1.0.2k-fips | CAfile: /etc/pki/tls/cert.pem -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem CApath: /etc/pki/tls/certs |
Ubuntu 18.04 | OpenSSL 1.1.1 | CAfile: なし? CApath: /usr/lib/ssl/certs -> /etc/ssl/certs/ |
以下、確認の詳細。
CentOS 6
[root@centos6 ~]# openssl version -a OpenSSL 1.0.1e-fips 11 Feb 2013 built on: Wed Aug 14 16:32:19 UTC 2019 platform: linux-x86_64 options: bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) idea(int) blowfish(idx) compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -DTERMIO -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -Wa,--noexecstack -DPURIFY -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM OPENSSLDIR: "/etc/pki/tls" engines: rdrand dynamic
OPENSSLDIRは/etc/pki/tlsとなっている。 確認してみる。
[root@centos6 ~]# ll /etc/pki/tls total 24 lrwxrwxrwx. 1 root root 19 Nov 21 16:58 cert.pem -> certs/ca-bundle.crt drwxr-xr-x. 2 root root 4096 May 4 22:00 certs drwxr-xr-x. 2 root root 4096 Nov 21 17:01 misc -rw-r--r--. 1 root root 10906 Jul 2 2019 openssl.cnf drwxr-xr-x. 2 root root 4096 Aug 14 2019 private
cert.pemはシンボリックリンクになっているので、certs配下を確認。
[root@centos6 ~]# ll /etc/pki/tls/certs total 1164 -rw-r--r--. 1 root root 755417 May 4 01:57 ca-bundle.crt -rw-r--r--. 1 root root 418126 Feb 28 2018 ca-bundle.trust.crt -rwxr-xr-x. 1 root root 610 Aug 14 2019 make-dummy-cert -rw-r--r--. 1 root root 2242 Aug 14 2019 Makefile -rwxr-xr-x. 1 root root 829 Aug 14 2019 renew-dummy-cert
ca-bundle.crtの実体があった。
CentOS 7
[root@localhost ~]# openssl version -a OpenSSL 1.0.2k-fips 26 Jan 2017 built on: reproducible build, date unspecified platform: linux-x86_64 options: bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) idea(int) blowfish(idx) compiler: gcc -I. -I.. -I../include -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -Wa,--noexecstack -DPURIFY -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DRC4_ASM -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM OPENSSLDIR: "/etc/pki/tls" engines: rdrand dynamic
OPENSSLDIRはCentOS6同様、/etc/pki/tlsになっている。
[root@localhost ~]# ll /etc/pki/tls total 12 lrwxrwxrwx. 1 root root 49 May 4 04:46 cert.pem -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem drwxr-xr-x. 2 root root 138 May 5 05:11 certs drwxr-xr-x. 2 root root 74 Jun 1 2019 misc -rw-r--r--. 1 root root 10923 May 4 04:50 openssl.cnf drwxr-xr-x. 2 root root 45 Apr 11 12:26 private
cert.pemの実体はCentOS6と異なり、例のupdate-ca-trustによって管理されている場所にあるようだ。 また、certsディレクトリは以下のとおり。
[root@localhost ~]# ll /etc/pki/tls/certs total 16 lrwxrwxrwx. 1 root root 49 May 4 12:51 ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem lrwxrwxrwx. 1 root root 55 Jun 1 2019 ca-bundle.trust.crt -> /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt -rw-------. 1 root root 1468 Apr 11 08:28 localhost.crt -rwxr-xr-x. 1 root root 610 Mar 12 2019 make-dummy-cert -rw-r--r--. 1 root root 2516 Mar 12 2019 Makefile -rwxr-xr-x. 1 root root 829 Mar 12 2019 renew-dummy-cert
curl用と思われるca-bundle.crt(シンボリックリンク)等が入っているが、Ubuntuと違い、信頼済みルートCA証明書のシンボリックリンク一覧は入っていない。openssl s_clientは当ディレクトリ配下の{hash}.0ファイルのみしか見ないため、これらの.crtファイルは無視される模様。
Ubuntu 18.04
root@ubuntu-bionic:~# openssl version -a OpenSSL 1.1.1 11 Sep 2018 built on: Tue Nov 12 16:58:35 2019 UTC platform: debian-amd64 options: bn(64,64) rc4(16x,int) des(int) blowfish(ptr) compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-kxN_24/openssl-1.1.1=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPADLOCK_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2 OPENSSLDIR: "/usr/lib/ssl" ENGINESDIR: "/usr/lib/x86_64-linux-gnu/engines-1.1" Seeding source: os-specific
OPENSSLDIRは/usr/lib/ssl。
root@ubuntu-bionic:~# ll /usr/lib/ssl total 12 drwxr-xr-x 3 root root 4096 Dec 18 19:12 ./ drwxr-xr-x 68 root root 4096 May 3 11:50 ../ lrwxrwxrwx 1 root root 14 Apr 25 2018 certs -> /etc/ssl/certs/ drwxr-xr-x 2 root root 4096 Dec 18 19:12 misc/ lrwxrwxrwx 1 root root 20 Nov 12 16:58 openssl.cnf -> /etc/ssl/openssl.cnf lrwxrwxrwx 1 root root 16 Apr 25 2018 private -> /etc/ssl/private/
certsディレクトリ (-> /etc/ssl/certs)はあるが、cert.pemは見当たらない・・・。CAfileの設定なしでビルドされたから?試しに/usr/lib/ssl配下にcert.pemを作ってみたが、CAfileとしては認識されなかった。
Javaアプリケーションの場合
${JAVA_HOME}/jre/lib/security/cacertsが参照される、というのが定説。
それ以外に、
- Javaの実行時オプションでシステムプロパティ(javax.net.ssl.trustStore)を設定する
- プログラム内で設定する (システムプロパティやSSLSocketFactoryを使う方法等あり)
といった方法があるようだ。
Javaアプリケーションでは、信頼するCAを「(JREのディレクトリ)/lib/security/cacerts」キーストアか、「javax.net.ssl.trustStore」プロパティで指定されるキーストアによって管理しています。デフォルトではjavax.net.ssl.trustStoreプロパティは指定されませんので、「(JREのディレクトリ)/lib/security/cacerts」キーストアに証明書が登録されている、CAを信頼します。
11. 認証方式 (5) | TECHSCORE(テックスコア)
システムプロパティの"javax.net.ssl.trustStore"を設定した場合、そちらが参照される。システムプロパティの設定方法としては、Javaの実行時オプションとして設定する方法と、Javaプログラムの中で指定する方法がある。
Javaの実行時オプションとして設定する方法
実行時オプションとして、システムプロパティ"javax.net.ssl.trustStore"を設定する。
# java -Djavax.net.ssl.trustStore=<cacertsへのフルパス> -Djavax. net.ssl.trustStorePassword=changeit <class名>
Javaプログラムの中で指定する方法
上記サイトを参考にさせていただいてます。
システムプロパティ"javax.net.ssl.trustStore"を設定
System.setProperty("javax.net.ssl.trustStore", "キーストア(トラストストア)のパス"); System.setProperty("javax.net.ssl.trustStorePassword ", "パスワード(default: changeit)");
その他、SSLSocketFactoryを使って自力でゴリゴリと設定する方法もあるようだ。
まとめると、Javaアプリケーションがどのルート証明書を参照するかは「アプリの作り次第」である。アプリ内で設定していればその場所が参照されるし、していなければデフォルトの場所が参照される。
実際のデフォルト値はどうなっているか
アプリケーションや実行時オプションで指定しなかった場合のデフォルト参照先について、調査結果を先にまとめておく。
OS | Java | Javaが参照するルート証明書の場所 |
---|---|---|
CentOS 6.10 | OpenJDK 1.8.0 | ${JAVA_HOME}/jre/lib/security/cacerts -> /etc/pki/java/cacerts |
CentOS 7.6.1810 | OpenJDK 1.8.0 | /etc/pki/java/cacerts -> /etc/pki/ca-trust/extracted/java/cacerts |
Ubuntu 18.04 | OpenJDK 1.8.0 | ${JAVA_HOME}/jre/lib/security/cacerts -> /etc/ssl/certs/java/cacerts |
- CentOS7だけ異なり、${JAVA_HOME}/jre/lib/security/cacertsではなく、OSの証明書ストア(/etc/pki/java/cacerts)が参照されていた。理由は不明。
- 今回調査した環境ではいずれも${JAVA_HOME}/jre/lib/security/cacertsはOSの証明書ストアへのシンボリックリンクになっていたが、私が知っている某商用環境(RHEL6, 7 + Oracle JDK 1.8)では${JAVA_HOME}/jre/lib/security/cacertsに実体があった。これはOpenJDKとOracleJDKの違いかもしれない。この場合、curlやopensslでルート証明書のテストをして問題ないとなっても、javaだとエラーになるケースも想定されるため、注意が必要。
- OpenJDK 1.8.0しか調べていないので、他のバージョンだと変わるかもしれない。
CentOS6 + OpenJDK 1.8.0
OpenJDKはyumでインストールしたもの。
[root@centos6 ~]# cat /etc/redhat-release CentOS release 6.10 (Final) [root@centos6 ~]# java -version openjdk version "1.8.0_252" OpenJDK Runtime Environment (build 1.8.0_252-b09) OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)
${JAVA_HOME}/jre/lib/security/cacertsを確認。
[root@centos6 ~]# ll /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el6_10.x86_64/jre/lib/security/ total 64 -rw-r--r--. 1 root root 1273 Apr 27 08:19 blacklisted.certs lrwxrwxrwx. 1 root root 41 May 4 00:53 cacerts -> ../../../../../../../etc/pki/java/cacerts -rw-r--r--. 1 root root 2567 Apr 27 08:19 java.policy -rw-r--r--. 1 root root 45794 Apr 27 08:19 java.security -rw-r--r--. 1 root root 141 Apr 27 08:24 nss.cfg drwxr-xr-x. 4 root root 4096 May 2 00:24 policy
cacertsが/etc/pki/java/cacertsへのシンボリックリンクになっている。
[root@centos6 ~]# ll /etc/pki/java/cacerts -rw-r--r--. 1 root root 158194 May 4 00:44 /etc/pki/java/cacerts
こちらはシンボリックリンクではなく、実体がここにあるようだ。
ここで、Javaアプリケーションが本当に${JAVA_HOME}/jre/lib/security/cacertsを使っているか、念のためテストしてみる。テスト用のJavaアプリケーションとして、先ほども引用した、下記ページのコードを使わせてもらった。
http://a4dosanddos.hatenablog.com/entry/2015/03/29/125111
TestCertClient.java
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; public class TestCertClient { public static void main(String[] args) throws Exception { URL url = new URL("https://webserver"); HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); System.out.println("----- Headers -----"); Map<String, List<String>> headers = con.getHeaderFields(); for (String key : headers.keySet()) { System.out.println(key + ": " + headers.get(key)); } System.out.println(); System.out.println("----- Body -----"); BufferedReader reader = new BufferedReader(new InputStreamReader( con.getInputStream())); String body; while ((body = reader.readLine()) != null) { System.out.println(body); } reader.close(); con.disconnect(); } }
また、先ほどと同じように、Apache + mod_sslでオレオレサーバ証明書(CN=webserver)を設定したWebサーバが192.168.12.22(/etc/hostsにwebserverと登録)で動いている。webserver側では、下記のようなhtmlを返すよう設定しておく。
<html> <head> </head> <body> <p>Hello, World!</p> </body> </html>
オレオレ証明書はまだクライアントに設定していないので、${JAVA_HOME}/jre/lib/security/cacerts -> /etc/pki/java/cacertsの中身をkeytoolで確認すると、
[root@centos6 ~]# keytool -v -list -keystore /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el6_10.x86_64/jre/lib/security/cacerts -storepass changeit | grep webserver [root@centos6 ~]#
webserverは登録されていない。 この状態でJavaアプリから疎通確認してみる。
[root@centos6 ~]# javac TestCertClient.java [root@centos6 ~]# java TestCertClient ----- Headers ----- ----- Body ----- Exception in thread "main" 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.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1950) at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1945) at java.security.AccessController.doPrivileged(Native Method) at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1944) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1514) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268) at TestCertClient.main(TestCertClient.java:36) Caused by: 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:198) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1967) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:331) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:325) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1688) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:226) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1082) at sun.security.ssl.Handshaker.process_record(Handshaker.java:1010) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1079) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1388) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1416) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1400) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498) at sun.net.www.protocol.http.HttpURLConnection.getHeaderFields(HttpURLConnection.java:3084) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getHeaderFields(HttpsURLConnectionImpl.java:297) at TestCertClient.main(TestCertClient.java:28) 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:450) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:317) 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:1670) ... 14 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:445) ... 20 more
当然ながら、unable to find valid certification path to requested targetが出る。 今度はオレオレ証明書(カレントディレクトリにあるserver.crt)をcacertsに登録する。
[root@centos6 ~]# keytool -importcert -file server.crt -keystore /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el6_10.x86_64/jre/lib/security/cacerts -storepass changeit -alias webserver Owner: CN=webserver, O=Default Company Ltd, L=Default City, ST=tokyo, C=JP Issuer: CN=webserver, O=Default Company Ltd, L=Default City, ST=tokyo, C=JP Serial number: 9a6640c6cb0cb258 Valid from: Wed Apr 29 02:15:04 PDT 2020 until: Fri May 29 02:15:04 PDT 2020 Certificate fingerprints: MD5: 3A:B6:25:B4:F0:E2:C3:54:41:B8:0F:1B:2A:F9:76:EB SHA1: C9:C3:50:95:88:51:56:CB:28:7F:D0:E8:D5:56:9B:55:40:90:29:B5 SHA256: 3E:47:A0:0F:47:77:1C:F3:E6:0D:22:F2:D5:B7:2F:94:DE:AD:16:1E:75:83:78:0B:DE:27:57:7D:44:BD:DB:16 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: 2048-bit RSA key Version: 1 Trust this certificate? [no]: yes Certificate was added to keystore
cacertsの中身を確認してみる。
[root@centos6 ~]# keytool -v -list -keystore /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el6_10.x86_64/jre/lib/security/cacerts -storepass changeit | grep webserver Alias name: webserver Owner: CN=webserver, O=Default Company Ltd, L=Default City, ST=tokyo, C=JP Issuer: CN=webserver, O=Default Company Ltd, L=Default City, ST=tokyo, C=JP
webserverが登録されたので、Javaアプリから疎通確認。
[root@centos6 ~]# java TestCertClient ----- Headers ----- Keep-Alive: [timeout=5, max=100] Accept-Ranges: [bytes] null: [HTTP/1.1 200 OK] Server: [Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips] ETag: ["54-5a46d692b208a"] Connection: [Keep-Alive] Last-Modified: [Wed, 29 Apr 2020 12:51:46 GMT] Content-Length: [84] Date: [Tue, 05 May 2020 12:35:27 GMT] Content-Type: [text/html; charset=UTF-8] ----- Body ----- <html> <head> </head> <body> <p>Hello, World!</p> </body> </html>
無事接続できている。 この状態で、試しにcacertsのシンボリックリンクをリネームしてみる。
[root@centos6 ~]# cd /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el6_10.x86_64/jre/lib/security/ [root@centos6 security]# ll total 64 -rw-r--r--. 1 root root 1273 Apr 27 08:19 blacklisted.certs lrwxrwxrwx. 1 root root 41 May 2 00:24 cacerts -> ../../../../../../../etc/pki/java/cacerts -rw-r--r--. 1 root root 2567 Apr 27 08:19 java.policy -rw-r--r--. 1 root root 45794 Apr 27 08:19 java.security -rw-r--r--. 1 root root 141 Apr 27 08:24 nss.cfg drwxr-xr-x. 4 root root 4096 May 2 00:24 policy [root@centos6 security]# ln -nfs ../../../../../../../etc/pki/java/cacerts cacerts2 [root@centos6 security]# ll total 64 -rw-r--r--. 1 root root 1273 Apr 27 08:19 blacklisted.certs lrwxrwxrwx. 1 root root 41 May 2 00:24 cacerts -> ../../../../../../../etc/pki/java/cacerts lrwxrwxrwx. 1 root root 41 May 4 00:46 cacerts2 -> ../../../../../../../etc/pki/java/cacerts -rw-r--r--. 1 root root 2567 Apr 27 08:19 java.policy -rw-r--r--. 1 root root 45794 Apr 27 08:19 java.security -rw-r--r--. 1 root root 141 Apr 27 08:24 nss.cfg drwxr-xr-x. 4 root root 4096 May 2 00:24 policy [root@centos6 security]# unlink cacerts [root@centos6 security]# ll total 64 -rw-r--r--. 1 root root 1273 Apr 27 08:19 blacklisted.certs lrwxrwxrwx. 1 root root 41 May 4 00:46 cacerts2 -> ../../../../../../../etc/pki/java/cacerts -rw-r--r--. 1 root root 2567 Apr 27 08:19 java.policy -rw-r--r--. 1 root root 45794 Apr 27 08:19 java.security -rw-r--r--. 1 root root 141 Apr 27 08:24 nss.cfg drwxr-xr-x. 4 root root 4096 May 2 00:24 policy
この状態で再度疎通確認してみる。
[root@centos6 ~]# java TestCertClient ----- Headers ----- ----- Body ----- Exception in thread "main" javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1950) at sun.net.www.protocol.http.HttpURLConnection$10.run(HttpURLConnection.java:1945) at java.security.AccessController.doPrivileged(Native Method) at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1944) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1514) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:268) at TestCertClient.main(TestCertClient.java:36) Caused by: javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty at sun.security.ssl.Alerts.getSSLException(Alerts.java:214) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1967) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1924) at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1907) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1423) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1400) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1570) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498) at sun.net.www.protocol.http.HttpURLConnection.getHeaderFields(HttpURLConnection.java:3084) at sun.net.www.protocol.https.HttpsURLConnectionImpl.getHeaderFields(HttpsURLConnectionImpl.java:297) at TestCertClient.main(TestCertClient.java:28) Caused by: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty at sun.security.validator.PKIXValidator.<init>(PKIXValidator.java:104) at sun.security.validator.Validator.getInstance(Validator.java:181) at sun.security.ssl.X509TrustManagerImpl.getValidator(X509TrustManagerImpl.java:318) at sun.security.ssl.X509TrustManagerImpl.checkTrustedInit(X509TrustManagerImpl.java:179) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:193) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:132) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1670) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:226) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1082) at sun.security.ssl.Handshaker.process_record(Handshaker.java:1010) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1079) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1388) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1416) ... 8 more Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:200) at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:120) at java.security.cert.PKIXBuilderParameters.<init>(PKIXBuilderParameters.java:104) at sun.security.validator.PKIXValidator.<init>(PKIXValidator.java:102) ... 20 more
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-emptyが出たので、やはりjavaは${JAVA_HOME}/jre/lib/security/cacerts -> /etc/pki/java/cacertsを使っているようだ。
CentOS7 + OpenJDK 1.8.0
OpenJDKはyumでインストールしたもの。
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.6.1810 (Core) [root@localhost ~]# java -version openjdk version "1.8.0_242" OpenJDK Runtime Environment (build 1.8.0_242-b08) OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
${JAVA_HOME}/jre/lib/security/cacertsを確認。
[root@localhost ~]# ll /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.el7.x86_64/jre/lib/security/ total 56 -rw-r--r--. 1 root root 1273 Apr 2 16:02 blacklisted.certs lrwxrwxrwx. 1 root root 41 May 4 07:19 cacerts -> ../../../../../../../etc/pki/java/cacerts -rw-r--r--. 1 root root 2567 Apr 2 16:02 java.policy -rw-r--r--. 1 root root 44792 Jan 1 2014 java.security -rw-r--r--. 1 root root 139 Apr 2 16:07 nss.cfg drwxr-xr-x. 4 root root 38 Apr 2 16:02 policy
CentOS6同様、/etc/pki/java/cacertsへのシンボリックリンクになっている。
[root@localhost ~]# ll /etc/pki/java/cacerts lrwxrwxrwx. 1 root root 40 May 4 07:03 /etc/pki/java/cacerts -> /etc/pki/ca-trust/extracted/java/cacerts
/etc/pki/java/cacertsはさらにシンボリックリンクとなっており、実体はupdate-ca-trustの管理下にあるようだ。
先ほどと同じように、${JAVA_HOME}/jre/lib/security/cacertsをリネームしてみる。
[root@localhost security]# ll total 56 -rw-r--r--. 1 root root 1273 Apr 2 16:02 blacklisted.certs lrwxrwxrwx. 1 root root 41 May 4 07:16 cacerts2 -> ../../../../../../../etc/pki/java/cacerts -rw-r--r--. 1 root root 2567 Apr 2 16:02 java.policy -rw-r--r--. 1 root root 44792 Jan 1 2014 java.security -rw-r--r--. 1 root root 139 Apr 2 16:07 nss.cfg drwxr-xr-x. 4 root root 38 Apr 2 16:02 policy
疎通確認。
[root@localhost ~]# java TestCertClient ----- Headers ----- Keep-Alive: [timeout=5, max=100] Accept-Ranges: [bytes] null: [HTTP/1.1 200 OK] Server: [Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips] ETag: ["54-5a46d692b208a"] Connection: [Keep-Alive] Last-Modified: [Wed, 29 Apr 2020 12:51:46 GMT] Content-Length: [84] Date: [Mon, 04 May 2020 07:17:01 GMT] Content-Type: [text/html; charset=UTF-8] ----- Body ----- <html> <head> </head> <body> <p>Hello, World!</p> </body> </html>
・・・なぜか通ってしまった。${JAVA_HOME}/jre/lib/security/cacertsは見ていない?
ちなみに、シンボリックリンク/etc/pki/java/cacertsをリネームしたらNG(java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty)になった。どこかでOSの方の証明書ストアを見に行くように設定がされたのだろうか?
Ubuntu 18.04 + OpenJDK 1.8.0
OpenJDKはaptでインストールしたもの。
root@ubuntu-bionic:~# cat /etc/os-release NAME="Ubuntu" VERSION="18.04.3 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.3 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic root@ubuntu-bionic:~# java -version openjdk version "1.8.0_252" OpenJDK Runtime Environment (build 1.8.0_252-8u252-b09-1~18.04-b09) OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)
${JAVA_HOME}/jre/lib/security/cacertsを確認。
root@ubuntu-bionic:~# ll /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security/ total 12 drwxr-xr-x 3 root root 4096 May 4 07:08 ./ drwxr-xr-x 8 root root 4096 May 2 08:02 ../ lrwxrwxrwx 1 root root 46 Apr 15 18:48 blacklisted.certs -> /etc/java-8-openjdk/security/blacklisted.certs lrwxrwxrwx 1 root root 27 May 4 07:08 cacerts -> /etc/ssl/certs/java/cacerts lrwxrwxrwx 1 root root 40 Apr 15 18:48 java.policy -> /etc/java-8-openjdk/security/java.policy lrwxrwxrwx 1 root root 42 Apr 15 18:48 java.security -> /etc/java-8-openjdk/security/java.security lrwxrwxrwx 1 root root 36 Apr 15 18:48 nss.cfg -> /etc/java-8-openjdk/security/nss.cfg drwxr-xr-x 4 root root 4096 May 2 08:02 policy/
/etc/ssl/certs/java/cacertsへのシンボリックリンクになっている。
root@ubuntu-bionic:~# ll /etc/ssl/certs/java/cacerts -rw-r--r-- 1 root root 156228 May 4 09:12 /etc/ssl/certs/java/cacerts
実体はこちらにある。ちなみにこのファイルはupdate-ca-certificatesの管理下であり、update-ca-certificatesを実行して信頼済みCA証明書を追加する際に、/etc/ssl/certs配下のca-certificates.crtおよびシンボリックリンク{hash}.0と共に更新されるようだ。
${JAVA_HOME}/jre/lib/security/cacertsをリネームしてみる。
root@ubuntu-bionic:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/security# ll total 12 drwxr-xr-x 3 root root 4096 May 5 13:38 ./ drwxr-xr-x 8 root root 4096 May 2 08:02 ../ lrwxrwxrwx 1 root root 46 Apr 15 18:48 blacklisted.certs -> /etc/java-8-openjdk/security/blacklisted.certs lrwxrwxrwx 1 root root 27 May 5 13:38 cacerts2 -> /etc/ssl/certs/java/cacerts lrwxrwxrwx 1 root root 40 Apr 15 18:48 java.policy -> /etc/java-8-openjdk/security/java.policy lrwxrwxrwx 1 root root 42 Apr 15 18:48 java.security -> /etc/java-8-openjdk/security/java.security lrwxrwxrwx 1 root root 36 Apr 15 18:48 nss.cfg -> /etc/java-8-openjdk/security/nss.cfg drwxr-xr-x 4 root root 4096 May 2 08:02 policy/
疎通確認。
root@ubuntu-bionic:~# java TestCertClient ----- Headers ----- ----- Body ----- Exception in thread "main" javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty ...以下略
the trustAnchors parameter must be non-emptyが出たので、Javaは${JAVA_HOME}/jre/lib/security/cacertsを参照しているようだ。
まとめ
結論的に、調べた範囲だとOS標準の場所を参照しているように見える(ただしOS標準の場所はディストリビューションとバージョンによって異なる)。アプリの作りとビルド時の設定にも依存するので、証明書の有効性を確認する時はそこまで確認する必要がある。
【curl】
OS | アプリとバージョン | デフォルトで参照するルート証明書の場所 |
---|---|---|
CentOS 6.10 | curl 7.19.7 | CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none |
CentOS 7.6.1810 | curl 7.29.0 | CAfile: /etc/pki/tls/certs/ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem CApath: none |
Ubuntu 18.04 | curl 7.58.0 | CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs |
【openssl】
OS | アプリとバージョン | デフォルトで参照するルート証明書の場所 |
---|---|---|
CentOS 6.10 | OpenSSL 1.0.1e-fips | CAfile: /etc/pki/tls/cert.pem -> /etc/pki/tls/certs/ca-bundle.crt CApath: /etc/pki/tls/certs |
CentOS 7.6.1810 | OpenSSL 1.0.2k-fips | CAfile: /etc/pki/tls/cert.pem -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem CApath: /etc/pki/tls/certs |
Ubuntu 18.04 | OpenSSL 1.1.1 | CAfile: なし? CApath: /usr/lib/ssl/certs -> /etc/ssl/certs/ |
【Java】
OS | アプリとバージョン | デフォルトで参照するルート証明書の場所 |
---|---|---|
CentOS 6.10 | OpenJDK 1.8.0 | ${JAVA_HOME}/jre/lib/security/cacerts -> /etc/pki/java/cacerts |
CentOS 7.6.1810 | OpenJDK 1.8.0 | /etc/pki/java/cacerts -> /etc/pki/ca-trust/extracted/java/cacerts |
Ubuntu 18.04 | OpenJDK 1.8.0 | ${JAVA_HOME}/jre/lib/security/cacerts -> /etc/ssl/certs/java/cacerts |