Java 에서 ValidatorException 등 인증서 관련 에러 해결 - keystore에 SSL/TLS 인증서를 import 하기
TL;DR
Java PKIX path building failed: 에러 해결 를 참고하세요.
증상
java 에서 HTTPS 로 remote 사이트에 연결시 다음과 같은 Exception 이 발생
Caused by: javax.naming.CommunicationException: simple bind failed: <server-name>
[Root exception is 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\]
원인
다음과 같이 여러 가지 원인이 있을 수 있다.
- 연결하려는 remote site의 인증서가 신뢰하는 인증기관 인증서 목록(keystore)에 없음
- 서버/클라이언트간 사용하려는 SSL/TLS 버전이 맞지 않음(Ex:TLS 1.0 만 지원하는 서버에 1.2로 hand shaking 요청등)
- SSL/TLS 통신에 사용하려는 cipher suite 가 오래되거나 지원하지 않음. (Ex: JDK 1.8 부터는 RC4 를 사용하려고 하면 에러 발생)
- 웹 브라우저의 경우 인증서 경로 설정을 참고하여 웹 서버에 Intermediate CA certificate 를 설치한다.
무료 SSL 인증서를 발급해 주는 Let's encrypt 의 CA 인증서는 Java VM 에 포함되어 있지 않으므로 Let's encrypt 에서 발급 받은 SSL 인증서를 Java 에서 사용할 경우에도 위와 같은 에러가 발생한다.
해결
1번 원인일 경우 현재 구동되는 JDK 의 keystore에 상대방 인증서를 넣어줘야 함
CMD 에서 처리
gist 에서 InstallCert.Java 를 다운로드
curl -O https://gist.githubusercontent.com/lesstif/cd26f57b7cfd2cd55241b20e05b5cd93/raw/InstallCert.java
CODE컴파일
javac InstallCert.java
CODEInstallCert 구동
# localhost 에 SSL 인증서를 받아올 호스트명을 입력 java -cp ./ InstallCert lesstif.com
BASH다음과 같은 화면이 나오면 1을 눌러서 인증서 저장
ssl importCaused by: java.lang.UnsupportedOperationException
at InstallCert$SavingTrustManager.getAcceptedIssuers(InstallCert.java:183)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(SSLContextImpl.java:926)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:872)
...Server sent 2 certificate(s):
1 Subject CN=wiki.modernpug.org
Issuer CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
sha1 47 0a 15 c4 5d ed 62 0a 4b 18 d5 d8 58 14 42 5d 36 e0 d5 8f
md5 3a 0d ab ce 27 be dd bd e5 c1 d5 e8 b6 25 aa eb2 Subject CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Issuer CN=DST Root CA X3, O=Digital Signature Trust Co.
sha1 e6 a3 b4 5b 06 2d 50 9b 33 82 28 2d 19 6e fe 97 d5 95 6c cb
md5 b1 54 09 27 4f 54 ad 8f 02 3d 3b 85 a5 ec ec 5dEnter certificate to add to trusted keystore or 'q' to quit: [1]
2서버가 2 개의 인증서를 전송했는데 2번째가 Let's Encrypt 의 CA 인증서이므로 2번을 선택해서 저장해야 한다.
다음과 같은 메시지가 나오고 저장됨. keystore 명과 alias 명을 기억
Added certificate to keystore 'jssecacerts' using alias 'letsencrypt'
keytool 로 keystore에서 인증서 추출 (KeyStore의 암호는 changeit 이라 가정!)
## alias 옵션뒤에 위의 alias명 입력 keytool -exportcert -keystore jssecacerts -storepass changeit -file output.cert -alias letsencrypt
BASH-alias 옵션 뒤에 파라미터는 5번에서 저장된 alias(letsencrypt) 를 입력
현재 JDK 의 keystore에 cert import
## JAVA_HOME=/usr/lib/jvm/java-11-openjdk sudo keytool -importcert -keystore ${JAVA_HOME}/lib/security/cacerts -storepass changeit -file output.cert -alias letsencrypt
BASH이미 존재할 경우 다음 명령어로 삭제
sudo keytool -delete -alias letsencrypt -keystore ${JAVA_HOME}/lib/security/cacerts -storepass changeit
Portecle GUI 사용
CA 별 Intermediate 인증서 등록
Comodo
comodo 의 root ca 인증서는 JVM에 포함되어 있지만 CA의 DN이 (CN = COMODO RSA Domain Validation Secure Server CA) 인경우 미포함이므로 keystore 에 넣어 주거나 웹 서버에 SSL 경로 구성을 해야 함(https://lesstif.gitbook.io/web-service-hardening/ssl-tls-https#undefined-5 참고)
CA 인증서 다운로드
curl -o comodo-domainca.crt https://gist.githubusercontent.com/lesstif/3fdfc2086b9834dbfbcd4461bfb68cd1/raw/76d3614bf86838a24343d0fddbb8ea39a9f1524c/comodo-domainca.crt
CODEROOT CA 인증서 다운로드
curl -o comodo-rootca.crt https://gist.githubusercontent.com/lesstif/3fdfc2086b9834dbfbcd4461bfb68cd1/raw/76d3614bf86838a24343d0fddbb8ea39a9f1524c/comodo-rootca.crt
CODE- keystore 에 import 하거나 웹 서버에 인증서 경로 구성
코모도의 전체 CA 인증서 목록은 https://ssl.comodo.com/support/which-is-root-which-is-intermediate.php 를 참고
AlphaSSL
저렴한 https://www.alphassl.com 의 경우 Java 의 Keystore 에 포함되지 않았으므로 수작업으로 인증서를 넣어줘야 한다.
Alpha SSL root 인증서 다운로드
curl -o root.cer https://www.alphassl.com/support/roots/root.pem
CODEhttps://www.alphassl.com/support/install-root-certificate.html 에 연결하여 발급한 인증서의 ca 인증서를 ca.cer 로 저장 (Ex: SHA-256 - Orders March 31, 2014 and After)
-----BEGIN CERTIFICATE-----
MIIETTCCAzWgAwIBAgILBAAAAAABRE7wNjEwDQYJKoZIhvcNAQELBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xNDAyMjAxMDAw
MDBaFw0yNDAyMjAxMDAwMDBaMEwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMSIwIAYDVQQDExlBbHBoYVNTTCBDQSAtIFNIQTI1NiAtIEcy
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2gHs5OxzYPt+j2q3xhfj
kmQy1KwA2aIPue3ua4qGypJn2XTXXUcCPI9A1p5tFM3D2ik5pw8FCmiiZhoexLKL
dljlq10dj0CzOYvvHoN9ItDjqQAu7FPPYhmFRChMwCfLew7sEGQAEKQFzKByvkFs
MVtI5LHsuSPrVU3QfWJKpbSlpFmFxSWRpv6mCZ8GEG2PgQxkQF5zAJrgLmWYVBAA
cJjI4e00X9icxw3A1iNZRfz+VXqG7pRgIvGu0eZVRvaZxRsIdF+ssGSEj4k4HKGn
kCFPAm694GFn1PhChw8K98kEbSqpL+9Cpd/do1PbmB6B+Zpye1reTz5/olig4het
ZwIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
AQAwHQYDVR0OBBYEFPXN1TwIUPlqTzq3l9pWg+Zp0mj3MEUGA1UdIAQ+MDwwOgYE
VR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3dy5hbHBoYXNzbC5jb20vcmVw
b3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWdu
Lm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6
Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAfBgNVHSMEGDAWgBRge2YaRQ2X
yolQL30EzTSo//z9SzANBgkqhkiG9w0BAQsFAAOCAQEAYEBoFkfnFo3bXKFWKsv0
XJuwHqJL9csCP/gLofKnQtS3TOvjZoDzJUN4LhsXVgdSGMvRqOzm+3M+pGKMgLTS
xRJzo9P6Aji+Yz2EuJnB8br3n8NA0VgYU8Fi3a8YQn80TsVD1XGwMADH45CuP1eG
l87qDBKOInDjZqdUfy4oy9RU0LMeYmcI+Sfhy+NmuCQbiWqJRGXy2UzSWByMTsCV
odTvZy84IOgu/5ZR8LrYPZJwR2UcnnNytGAMXOLRc3bgr07i5TelRS+KIz6HxzDm
MTh89N1SyvNTBCVXVmaU6Avu5gMUTu79bZRknl7OedSyps9AsUSoPocZXun4IRZZ
Uw==
-----END CERTIFICATE-----Key store 에 import
keytool -import -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -alias alpharoot -file root.cer -storepass changeit -noprompt keytool -import -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -alias alphaca -file ca.cer -storepass changeit -noprompt
CODE
let's encrypt 인증서
무료 인증서 발급해 주는 https://letsencrypt.org 일 경우
전체 인증서 체인은 https://letsencrypt.org/certificates/ 참고
root cert 다운로드
curl -o root.cer https://letsencrypt.org/certs/isrgrootx1.pem
CODE발급받은 인증서에 맞는 ca cert download(Ex: Let’s Encrypt Authority X3 (IdenTrust cross-signed):)
curl -o ca.cer https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
CODEKey store 에 import
keytool -import -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -alias letsroot -file root.cer -storepass changeit -noprompt keytool -import -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -alias letsca -file ca.cer -storepass changeit -noprompt
CODE
확인
- 해당 사이트에 웹 브라우저로 연결하여 문제가 해결되었는지 확인
openssl 명령창으로 SSL 인증서 정보 확인
openssl s_client -connect localhost:443 -showcerts
CODEcurl 로 연결해서 정보 확인
curl -v -k -I https://mysite.example.com
CODE
주의 사항
WAS 기반의 서비스일 경우 JDK 설정이 변경되었으므로 재구동이 필요함
JDK나 JRE 가 upgrade 될 경우 위의 작업을 다시 시행해야 함
같이 보기
- java keytool 사용법 - Keystore 생성, 키쌍 생성, 인증서 등록 및 관리
- OpenSSL 자주 쓰는 명령어(command) 및 사용법, tip 정리
- curl 에 신뢰하는 인증기관 인증서(CA Cert) 추가하기
Ref
- received-fatal-alert-handshake-failure-through-sslhandshakeexception
- http://stackoverflow.com/questions/3685548/java-keytool-easy-way-to-add-server-cert-from-url-port
- atlassian - SunCertPathBuilderException
- 06-no-more-unable-find-valid-certification-path-requested-target
- https://stackoverflow.com/questions/5871279/java-ssl-and-cert-keystore