Java์์ PKIX path building failed๊ฐ ๋ ๋
์ด ๊ธ์ ์์ 11๊ฐ ์
- ๋ฉ์์ง ์ฝ๊ธฐ
- ์ง๋จ ์์
- ์์ธ 1. ์๋ ์๋ฒ๊ฐ ์ค๊ฐ ์ธ์ฆ์๋ฅผ ์ ๋ณด๋ธ๋ค
- ์์ธ 2. ์ฌ์ค CA๋ ์์ฒด์๋ช ์ธ์ฆ์
- ์์ธ 3. ์ค๋๋ JDK์ cacerts
- ์์ธ 4. TLS๋ฅผ ๊ฒ์ฌํ๋ ์ฌ๋ด ๋ณด์ ์ฅ๋น
- ์์ธ 5. ์๋ชป ์ง์ ํ trustStore
- cacerts ์ค๋ฌด
- Tomcat, Spring Boot, ๋ฐฐ์น์์
- ๊ฒ์ฆ์ ๋ ์ฝ๋๊ฐ ๋ณด์ด๋ฉด
- ์ฐธ์กฐ
Java ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ธ๋ถ HTTPS API๋ฅผ ๋ถ๋ฅด๋ค ์ด๋ฐ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ณ ๋ฉ์ถ๋ค.
javax.net.ssl.SSLHandshakeException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
๊ฐ์ ์ฃผ์๊ฐ ๋ธ๋ผ์ฐ์ ์์๋ ์ด๋ฆฌ๊ธฐ๋ ํด์ ๋ ํท๊ฐ๋ฆฐ๋ค. ๊ฒ์ํ๋ฉด ๋์ค๋ ๋ต์ ๋๊ฐ ๋ ์ค ํ๋๋ค. ์๋ฒ
์ธ์ฆ์๋ฅผ cacerts์ ๋ฃ์ผ๋ผ๊ฑฐ๋, ์ธ์ฆ์ ๊ฒ์ฆ์ ๋๋ผ๊ฑฐ๋. ์์ ๊ฒ์ ์์ธ์ ๋ฐ๋ผ ๋ง๊ธฐ๋ ํ๊ณ ํ๋ฆฌ๊ธฐ๋
ํ๋ค. ๋ค์ ๊ฒ์ ์ธ์ ๋ ํ๋ ธ๋ค.
๋ฉ์์ง๊ฐ ๋งํ๋ ๊ฑด ํ๋๋ค. JVM์ด ์๋ฒ ์ธ์ฆ์์์ ์ถ๋ฐํด ์๊ธฐ๊ฐ ์ ๋ขฐํ๋ ์ต์ปค๊น์ง ๊ฒฝ๋ก๋ฅผ ๋ง๋ค์ง ๋ชปํ๋ค.
์๋ฒ๊ฐ ์ฒด์ธ์ ๋ ๋ณด๋์ ์๋, JVM์ ์ ๋ขฐ ๋ชฉ๋ก์ด ๋ชจ์๋ ์๋, ์ค๊ฐ์์ ๋ค๋ฅธ ์ธ์ฆ์๊ฐ ๋ผ์ด๋ค์์ ์๋
์๋ค. ์ค๊ฐ ์ธ์ฆ์๊ฐ ๋น ์ก์ผ๋ฉด ์๋ฒ๋ฅผ ๊ณ ์น๊ณ , JDK๊ฐ ์ค๋๋์ผ๋ฉด ์
๋ฐ์ดํธํ๊ณ , trustStore๋ฅผ ์๋ชป ์ง์ ํ์ผ๋ฉด
JVM ์ต์
์ ๊ณ ์น๋ค. ๋ฉ์์ง๋ง ๋ณด๊ณ cacerts์ ์ํฌํธ๋ถํฐ ํ๋ฉด ๊ฐ์ ์ฅ์ ๊ฐ ๋ํ์ด๋๋ค. ์ด๋ ์ชฝ์ธ์ง ๊ฐ๋ฅด๋
๊ฒ ๋จผ์ ๋ค.
๋ธ๋ผ์ฐ์ ์์๋ ๊ฐ์ ์ฑ๊ฒฉ์ ์๋ฌ๊ฐ NET::ERR_CERT_AUTHORITY_INVALID๋ก ๋์ค๊ณ ์์ธ ๊ฐ๋๋ ์๋น ๋ถ๋ถ
๊ฒน์น๋ค. ๋ธ๋ผ์ฐ์ ์ชฝ์ NET::ERR_CERT_AUTHORITY_INVALID ์์ธ๋ณ ํด๊ฒฐ์
์๋ค.
๋ฉ์์ง ์ฝ๊ธฐ
๋ ๋ฌธ์ฅ์ ์ด์ด ๋ถ์ธ ํํ์ด๊ณ , ๊ฐ๊ฐ OpenJDK์ ๋ค๋ฅธ ํด๋์ค๊ฐ ๋ง๋ ๋ค.
| ์กฐ๊ฐ | ๋์ง๋ ๊ณณ | ๋ป |
|---|---|---|
PKIX path building failed: |
PKIXValidator |
CertPathBuilder๊ฐ ์คํจํ์ ๋ ๋ถ์ด๋ ์ ๋์ด |
unable to find valid certification path to requested target |
SunCertPathBuilder |
์ ๋ขฐ ์ต์ปค๊น์ง ์ด์ด์ง๋ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ง ๋ชปํจ |
๊ฒฝ๋ก๋ฅผ ๋ง๋๋ ๋จ๊ณ์์ ์คํจํ๋ค๋ ๋ป์ด๋ค. ๋น์ทํด ๋ณด์ฌ๋ ์คํจํ ๋จ๊ณ๊ฐ ๋ค๋ฅธ ๋ฉ์์ง๊ฐ ๋ ์๋ค. ์๋ ์ง๋จ๊ณผ ์์ธ์ ์ฒซ ์ค, ๊ฒฝ๋ก ๊ตฌ์ฑ์์ ์คํจํ ๊ฒฝ์ฐ์๋ง ํด๋นํ๋ค.
| ๋ฉ์์ง | ์คํจ ๋จ๊ณ |
|---|---|
PKIX path building failed + unable to find valid certification path |
๊ฒฝ๋ก ๊ตฌ์ฑ |
PKIX path validation failed |
๊ฒฝ๋ก๋ ๋ง๋ค์๊ณ ๊ฒ์ฆ์์ ์คํจ |
TLS Server certificate issued after ... anchored by a distrusted legacy ... root CA |
CA distrust ์ ์ฑ |
PKIX path validation failed๋ ๊ฐ์ PKIXValidator ํด๋์ค์ ๋ฐ๋ก ์๋ ๋ฉ์์ง๋ค. ๋๋ถ๋ถ ๋ง๋ฃ, ํ์ง,
์๊ณ ๋ฆฌ์ฆ ์ ์ฝ ๊ฐ์ ๊ฒ์ฆ ๋จ๊ณ ๋ฌธ์ ๋ผ์ ์ ๋ขฐ ์ ์ฅ์์ ๋ฃจํธ๋ฅผ ๋ ๋ฃ์ด๋ ํ๋ฆฌ์ง ์๋๋ค. ์์ธ๋
Path does not chain with any of the trust anchors์ฒ๋ผ ๊ฐ์ ์ด๋ฆ์ ์ ๋ฃจํธ์ ์ถฉ๋ํ๋ ๊ฒฝ์ฐ๋ก, ์ฌ๋ฐ๋ฅธ
๋ฃจํธ๋ก ๊ฐฑ์ ํ๋ฉด ํ๋ฆฐ๋ค. ์์ธ์ ๋ฉ์์ง ๋ค์ ๋ถ์ ์์ธ ์ค๋ช
์ ์ ํ ์๋ค.
distrust ๋ฉ์์ง๋ JDK๊ฐ ํน์ CA ๋ฃจํธ๋ก ์ด์ด์ง๋ ์ธ์ฆ์ ๊ฐ์ด๋ฐ ์ผ์ ๋ ์ง ์ดํ ๋ฐ๊ธ๋ถ์ ์ผ๋ถ๋ฌ ๊ฑฐ๋ถํ ๊ฒฐ๊ณผ๋ค. Oracle JDK 21 ๋ฆด๋ฆฌ์ค ๋ ธํธ ๊ธฐ์ค์ผ๋ก Entrust ๋ฃจํธ๋ 2024-11-11 ์ดํ ๋ฐ๊ธ๋ถ(21.0.5), Chunghwa ๋ ๊ฑฐ์ ๋ฃจํธ๋ 2026-03-17 ์ดํ ๋ฐ๊ธ๋ถ(21.0.11)์ด ๋์์ด๋ค. ๋ฉ์์ง์ ๋ ์ง์ CA ์ด๋ฆ์ด ๊ทธ๋๋ก ์ฐํ์ ๊ตฌ๋ถํ๊ธฐ ์ฝ๋ค.
TLS Server certificate issued after 2026-03-17 and anchored by a distrusted legacy Chunghwa root CA: ...
์ง๋จ ์์
์ธ ๋จ๊ณ๋ก ๋ณธ๋ค. ๋ค๋ก ๊ฐ์๋ก ์ถ๋ ฅ์ด ๋ง์์ง๊ณ , ๋๋ถ๋ถ์ 1)์ด๋ 2)์์ ๊ฐ๋ฆฐ๋ค.
1) ์๋ฒ๊ฐ ์ค์ ๋ก ๋ณด๋ด๋ ์ฒด์ธ (openssl)
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null
-showcerts๋ ์๋ฒ๊ฐ ๋ณด๋ธ ์ธ์ฆ์๋ง, ๋ณด๋ธ ์์๋๋ก ๋ณด์ฌ ์ค๋ค. ๊ฒ์ฆ์ ๊ฑฐ์น ์ฒด์ธ์ด ์๋๋ผ ์๋ฒ๊ฐ ๋ณด๋ธ
๋ชฉ๋ก ๊ทธ๋๋ก๋ค. ์ถ๋ ฅ ์๋ถ๋ถ Certificate chain ์๋๋ฅผ ์ฝ๋๋ค.
Certificate chain
0 s:CN = api.example.com
i:C = US, O = Example CA, CN = Example Intermediate
a:PKEY: ... (OpenSSL 3.x)
v:NotBefore: ...; NotAfter: ...
-----BEGIN CERTIFICATE-----
(PEM ์๋ต)
-----END CERTIFICATE-----
1 s:C = US, O = Example CA, CN = Example Intermediate
i:C = US, O = Example CA, CN = Example Root
(์ดํ ์๋ต)
OpenSSL ๋ฒ์ ์ ๋ฐ๋ผ CN = api.example.com์ฒ๋ผ ๊ณต๋ฐฑ์ด ๋ค์ด๊ฐ๊ธฐ๋ ํ๊ณ , CN=api.example.com์ฒ๋ผ ๋ถ์ด
๋์ค๊ธฐ๋ ํ๋ค.
๋ช ์ฅ์ ๋ณด๋๋์ง๋ง ๋นจ๋ฆฌ ์ธ๋ ค๋ฉด:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
| grep -c "BEGIN CERTIFICATE"
๊ฒฐ๊ณผ๋ ์ด๋ ๊ฒ ์ฝ๋๋ค.
| ๋ณด์ด๋ ๊ฒ | ํ๋จ |
|---|---|
0 ๋ฒ ํ ์ฅ๋ฟ์ด๊ณ i:๊ฐ ๋ฃจํธ๊ฐ ์๋ |
์ค๊ฐ ์ธ์ฆ์ ๋๋ฝ (์์ธ 1) |
i:๊ฐ ์ฌ๋ด CA ์ด๋ฆ |
์ฌ์ค CA (์์ธ 2) |
0 ๋ฒ์ s:์ i:๊ฐ ๊ฐ์ |
์์ฒด์๋ช (์์ธ 2) |
| ๊ณต๊ฐ CA ์ฒด์ธ์ด ์จ์ ํจ | ์๋ฒ๋ ์ ์. JVM ์ชฝ์ ๋ณธ๋ค (์์ธ 3ยท4ยท5) |
์๋ ์๋ฒ์ ์ธ์ฆ์ ์ํ๋ง ๋จผ์ ํ์ด๋ณด๋ ค๋ฉด ๋ฌด๋ฃ ์ง๋จ ํด์ ๋๋ฉ์ธ์ ๋ฃ์ด๋ ๋๋ค. ๋ฐ๊นฅ์์ ๋ณธ TLS
๊ฒ์ฆ ๊ฒฐ๊ณผ์ ๋ง๋ฃ์ผ์ด ํจ๊ป ๋์จ๋ค. ๋ค๋ง ๊ฑฐ๊ธฐ ๊ทธ๋ ค์ง๋ ์ฒด์ธ์ CT ๋ก๊ทธ์ AIA๋ก ์ฌ๊ตฌ์ฑํ ๊ฒ์ด๋ผ, ์๋ฒ๊ฐ
์ค์ ๋ก ๋ณด๋ด๋ ๋ชฉ๋ก์ -showcerts๋ก ํ์ธํ๋ค.
2) JVM์ด ๋๋ ์๋ฒ์์ ๋ณธ ์ธ์ฆ์ (keytool)
openssl์ ๋ณดํต ๋ด PC์์ ๋๋ฆฌ์ง๋ง ๋ฌธ์ ๋ JVM์ด ๋๋ ์๋ฒ์ ๋คํธ์ํฌ์์ ๋๋ค. ๊ทธ ์๋ฒ์์ keytool๋ก ๋ค์
๋ณธ๋ค. -sslserver๋ JDK 8 keytool์๋ ์๋ค.
keytool -printcert -sslserver api.example.com:443
# ํ๋ก์๋ฅผ ๊ฑฐ์ณ ๋๊ฐ๋ ์๋ฒ๋ผ๋ฉด
keytool -J-Dhttps.proxyHost=proxy.corp -J-Dhttps.proxyPort=8080 \
-printcert -sslserver api.example.com:443
ํฌํธ๋ฅผ ๋นผ๋ฉด 443์ด๋ค. -rfc๋ฅผ ๋ถ์ด๋ฉด PEM ๋ธ๋ก๋ง ์ถ๋ ฅํ๊ณ OwnerยทIssuer ์ค์ ์ฐ์ง ์์ผ๋, PEM ํ์ผ๋ก
์ ์ฅํ ๋๋ง ์ด๋ค.
์ฌ๊ธฐ์ ๋ณด์ด๋ ๋ฐ๊ธ์(Issuer)๊ฐ 1)์ ๊ฒฐ๊ณผ์ ๋ค๋ฅด๋ฉด ์ค๊ฐ์์ ์ธ์ฆ์๋ฅผ ๋ฐ๊ฟ ๋ผ์ด ๊ฒ์ด๋ค(์์ธ 4). ๋ณด์
์ฅ๋น ์ด๋ฆ์ด๋ ์ฌ๋ด CA ์ด๋ฆ์ด ๋ณด์ด๋ฉด ํนํ ๊ทธ๋ ๋ค.
3) JVM ๋ด๋ถ (๋๋ฒ๊ทธ ์ต์ )
์๋ฒ ์ชฝ์ด ์ ์์ผ๋ก ๋ณด์ด๋ฉด JVM์ด ๋ฌด์์ ์ ๋ขฐํ๊ณ ์๋์ง ๋ณธ๋ค.
java -Djavax.net.debug=ssl,handshake,trustmanager -jar app.jar
trustmanager๋ TrustManager ์ถ์ ์ ์ฐ๋๋ค. ์ด๋ค truststore๋ฅผ ์ฝ์๋์ง, ์๋ฒ์์ ๋ฐ์ ์ฒด์ธ์ด ๋ฌด์์ธ์ง๊ฐ
์ฌ๊ธฐ์ ๋์จ๋ค. ์๋ชป๋ trustStore(์์ธ 5)๋ ์ด ์ถ๋ ฅ์์ ๊ฐ์ฅ ๋นจ๋ฆฌ ๋๋ฌ๋๋ค.
์ด๋๊น์ง ์คํจํ๋์ง๋ ๋จ์๋ค. ๊ณต๊ฐ ์๋ฒ๊น์ง ์ ๋ถ ์คํจํ๋ฉด trustStore ์ง์ ์, truststore๋ฅผ ๋ฃ์๋๋ฐ ์ฌ์ค CA ์๋ฒ๋ง ์คํจํ๋ฉด trustStore ๊ฒฝ๋ก ์คํ๋ฅผ ์์ฌํ๋ค(๋ ๋ค ์์ธ 5). ํน์ CA์ ๊ณต๊ฐ ์๋ฒ๋ง ์คํจํ๋ฉด ์ค๋๋ cacerts(์์ธ 3)๋ค.
์ต์ ๊ท์น์ ์ด๋ ๋ค.
- ๊ตฌ๋ถ์(
,:)์ ์์๋ ์๊ด์๋ค ssl์ data, packet, plaintext๋ฅผ ๋บ SSL ๋๋ฒ๊ทธ ์ ์ฒด๋คsslํ์ ์ต์ :defaultctxhandshakekeygenkeymanagerpluggabilityrecordrespmgrsessionsessioncachesslctxtrustmanagerhandshake๋dataยทverbose๋ก,record๋plaintextยทpacket์ผ๋ก ๋ ์์ธํด์ง๋คall์ ์ ๋ถ ์ผ ๋ค. ์ถ๋ ฅ์ด ์์์ง๋ค-Djavax.net.debug๋ฅผ ๊ฐ ์์ด ์ฃผ๋ฉดSystem.Logger์javax.net.ssl๋ก ๊ธฐ๋ก๋๋ค(JDK 21 ๋ฌธ์ ๊ธฐ์ค)
๊ฒฝ๋ก๋ฅผ ๋ง๋๋ ๊ณผ์ ์์ฒด๋ฅผ ๋ณด๋ ค๋ฉด certpath ๋๋ฒ๊ทธ๋ฅผ ์ด๋ค.
java -Djava.security.debug=certpath -jar app.jar
PKIX CertPathBuilder์ CertPathValidator ๊ตฌํ์ ๋๋ฒ๊ทธ๊ฐ ์ผ์ง๋ค. ํ์ ์ต์
์ผ๋ก OCSP ๊ตํ์ ๋คํํ๋
ocsp์ ๋ ์์ธํ verbose๊ฐ ์๋ค.
์์ธ 1. ์๋ ์๋ฒ๊ฐ ์ค๊ฐ ์ธ์ฆ์๋ฅผ ์ ๋ณด๋ธ๋ค
์ง๋จ 1)์์ ์๋ฒ๊ฐ ์๊ธฐ ์ธ์ฆ์ ํ ์ฅ๋ง ๋ณด๋ด๋ ๊ฒฝ์ฐ๋ค.
TLS 1.3 ๊ท๊ฒฉ(RFC 8446)์ ์๋ฒ ์ธ์ฆ์๋ฅผ ๋ชฉ๋ก ๋งจ ์์ ๋๊ณ (MUST), ์ด์ด์ง๋ ์ธ์ฆ์๊ฐ ๋ฐ๋ก ์ ์ธ์ฆ์๋ฅผ ์ธ์ฆํ๋๋ก(SHOULD) ์ ํ๋ค. ์๋ตํด๋ ๋๋ ๊ฑด ์ ๋ขฐ ์ต์ปค, ์ฆ ๋ฃจํธ๋ฟ์ด๋ค. ์ค๊ฐ ์ธ์ฆ์๋ฅผ ๋นผ๋ ๊ฑด ๊ท๊ฒฉ์ด ํ์ฉํ๋ ์๋ต์ด ์๋๋ค.
๋ธ๋ผ์ฐ์ ์์ ์ด๋ฆฌ๋์ง๋ ๊ธฐ์ค์ด ๋ชป ๋๋ค. ์ฒด์ธ์ ๋ค๋ฃจ๋ ๋ฐฉ์์ด ํด๋ผ์ด์ธํธ๋ง๋ค ๋ค๋ฅด๋ค. ๊ทธ ์ฐจ์ด๋
ํฌ๋กฌ์ ๋๋๋ฐ ํ์ด์ดํญ์ค๋ง ์ธ์ฆ์ ์ค๋ฅ์ ๋ฐ๋ก ์ ์๋ค.
๊ธฐ์ค์ -showcerts์ ์ฐํ ๋ชฉ๋ก์ด๋ค.
Java๋ ๊ธฐ๋ณธ ์ค์ ์ผ๋ก๋ ๋น ์ง ์ค๊ฐ ์ธ์ฆ์๋ฅผ ์ฑ์ฐ์ง ์๋๋ค. ์ธ์ฆ์์ AIA ํ์ฅ์ ์ ํ ๋ฐ๊ธ์
์ฃผ์(caIssuers)๋ฅผ ๋ฐ๋ผ๊ฐ ๋ด๋ ค๋ฐ๋ ๊ธฐ๋ฅ์ด ์์ง๋ง ํธํ์ฑ ๋๋ฌธ์ ๊ธฐ๋ณธ์ผ๋ก ๊บผ์ ธ ์๊ณ ,
com.sun.security.enableAIAcaIssuers=true๋ก ์ผ์ผ ํ๋ค.
์ ์ฝ์ด ํ๋ ๋ ๋ถ์๋ค. Oracle JDK 21์ 21.0.10(2026-01-20)๋ถํฐ com.sun.security.allowedAIALocations
ํํฐ๋ฅผ ๋์
ํ๋๋ฐ, ๊ธฐ๋ณธ๊ฐ์ด ๋น์ด ์์ด์ ์ ๋ถ ๊ฑฐ๋ถํ๋ค. ์ผ๋ ์์ฑ๊ณผ URI ํ์ฉ ๊ท์น์ด ๋ ๋ค ์์ด์ผ AIA
์ฃผ์๋ฅผ ๋ฐ๋ผ๊ฐ๋ค.
๊ณ ์น ๊ณณ์ ์๋ฒ์ ์ฒด์ธ์ด๋ค.
์ฐ๋ฆฌ ์๋ฒ๋ผ๋ฉด ์๋ฒ ์ธ์ฆ์์ ์ค๊ฐ ์ธ์ฆ์๋ฅผ ํจ๊ป ๋ด๋ณด๋ด๋๋ก ์ค์ ํ๋ค. nginx๋ ๊ฐ์ ํ์ผ์ ์๋ฒ ์ธ์ฆ์, ์ค๊ฐ ์ธ์ฆ์ ์์ผ๋ก ๋ฃ๋๋ค(Nginx ๊ฐ์ด๋). Tomcat์ด ์๋ฒ๋ผ๋ฉด Tomcat ๊ฐ์ด๋๋๋ก fullchain์ผ๋ก keystore๋ฅผ ๋ง๋ ๋ค.
์๋(ํํธ๋, ์ธ๋ถ API) ์๋ฒ๋ผ๋ฉด -showcerts ์ถ๋ ฅ์ ๊ทผ๊ฑฐ๋ก ๋ถ์ฌ ์ฒด์ธ ์์ ์ ์์ฒญํ๋ค. โ์ฐ๋ฆฌ๋ง ์
๋๋คโ๊ฐ ์๋๋ผ โ์๋ฒ๊ฐ ํ ์ฅ๋ง ๋ณด๋ธ๋คโ๊ณ ๋งํด์ผ ๋นจ๋ฆฌ ๊ณ ์ณ์ง๋ค.
๋น์ฅ ๋ชป ๊ณ ์น๋ค๋ฉด ๊ทธ ์ค๊ฐ ์ธ์ฆ์๋ฅผ cacerts ์ฌ๋ณธ์ ๋ํ ์ฑ ์ ์ฉ truststore๋ก ๋ฒํธ ์๋ ์๋ค. ์ค๊ฐ
์ธ์ฆ์๋ง ๋ ํ์ผ์ ์ง์ ํ๋ฉด ์์ธ 5๊ฐ ๋๋ค. ์๋๊ฐ ๊ฐฑ์ ํ๋ฉด์ ์ค๊ฐ ์ธ์ฆ์๊ฐ ๋ฐ๋๋ฉด ๋ค์ ๊นจ์ง๋, ๊ธฐํ์
์ ํ ์์ ์กฐ์น๋ก๋ง ์ด๋ค.
์์ธ 2. ์ฌ์ค CA๋ ์์ฒด์๋ช ์ธ์ฆ์
์ฌ๋ด ์์คํ , ํํธ๋ ์ ์ฉ API, ๊ฐ๋ฐ ์๋ฒ๊ฐ ์ฌ์ค CA๋ ์์ฒด์๋ช ์ธ์ฆ์๋ฅผ ์ฐ๋ ๊ฒฝ์ฐ๋ค.
cacerts์๋ Oracle Java Root Certificate Program์ ๋ CA์ ๋ฃจํธ๊ฐ ๋ค์ด ์๋ค. ๋ชฉ๋ก์ ์๋ CA๋ฅผ ์ ๋ขฐํ๋ ค๋ฉด
๊ทธ CA ์ธ์ฆ์๋ฅผ ์ ๋ขฐ ์ธ์ฆ์๋ก ์ง์ ๋ฃ์ด์ผ ํ๋ค. ์ฌ๋ด CA์์ ์คํจํ๋ ๊ฑด ์ ์ ๋์์ด๋ค.
๋ฃ๊ธฐ ์ ์ ๋ฌด์์ ๋ฃ์์ง, ์ด๋์ ๋ฃ์์ง๋ฅผ ์ ํ๋ค.
๋ฌด์์ ๋ฃ๋
keytool -printcert -file corp-root-ca.crt
์ถ๋ ฅ๋ SHA-256 ์ง๋ฌธ์ ๋ฐ๊ธ์ฒ(์ฌ๋ด PKI ๋ด๋น, ํํธ๋์ฌ)์ ๋ค๋ฅธ ์ฑ๋๋ก ๋์กฐํ๋ค. Oracle keytool ๋ฌธ์๊ฐ ์ ๋ขฐ ์ธ์ฆ์๋ฅผ ์ํฌํธํ๊ธฐ ์ ์ ๋ช ์์ ์ผ๋ก ์๊ตฌํ๋ ์ ์ฐจ๋ค. ๋ฉ์ผ๋ก ๋ฐ์ ํ์ผ์ ์ง๋ฌธ์ ๊ฐ์ ๋ฉ์ผ ๋ณธ๋ฌธ์ผ๋ก ํ์ธํ๋ ๊ฑด ๋์กฐ๊ฐ ์๋๋ค.
keytool์ DER(๋ฐ์ด๋๋ฆฌ)๊ณผ PEM(-----BEGIN์ผ๋ก ์์ํ๋ Base64)์ ๋ ๋ค ๋ฐ์ผ๋, keytool์ ๋ฃ์ ๋๋
๋ณํํ ํ์๊ฐ ์๋ค. OS ์์คํ
ํธ๋ฌ์คํธ๋ ๋ค๋ฅด๋ค(์๋ Debian, Ubuntu ์ฐธ๊ณ ).
๋ฃ์ ๊ฒ์ ๋ฃจํธ(๋๋ ๋ฐ๊ธ CA) ์ธ์ฆ์๋ค. ์๋ฒ ์ธ์ฆ์(leaf)๋ฅผ ๋ฃ์ผ๋ฉด ๋น์ฅ์ ๋์ง๋ง ์๋๊ฐ ์ธ์ฆ์๋ฅผ ๊ฐฑ์ ํ๋ ๋ ๋ค์ ๊นจ์ง๋ค. ์์ฒด์๋ช ์ธ์ฆ์๋ ๊ทธ ์์ฒด๊ฐ ์ต์ปค๋ผ ๋ฃ์ ์๋ฐ์ ์๋๋ฐ, ์ฌ๋ฐ๊ธํ ๋๋ง๋ค ๋ชจ๋ ํด๋ผ์ด์ธํธ๋ฅผ ๋ค์ ์๋ด์ผ ํ๋ค. ์ค๋ ์ธ ์์คํ ์ด๋ฉด ์ฌ์ค CA๋ก ์ฎ๊ธฐ๋ ํธ์ด ๋ซ๋ค.
์ด๋์ ๋ฃ๋
JVM ์ ์ญ cacerts์ ๋ฃ์ผ๋ฉด ๊ทธ JDK๋ฅผ ์ฐ๋ ๋ชจ๋ ์ฑ์ด ์ด CA๋ฅผ ์ ๋ขฐํ๋ค. ํํธ๋ API ํ๋ ๋๋ฌธ์ด๋ผ๋ฉด ๋ฒ์๊ฐ
๋๋ฌด ๋๋ค. ๋ฒ์๊ฐ ์ข์ ๊ฒ๋ถํฐ ์ ์ผ๋ฉด ์ด๋ ๋ค.
| ๋ฐฉ๋ฒ | ์ํฅ ๋ฒ์ | ์ ํฉํ ๊ฒฝ์ฐ |
|---|---|---|
| Spring Boot SSL Bundle | ํน์ HTTP ํด๋ผ์ด์ธํธ ํ๋ | Spring Boot 3.1+ (RestClient๋ 3.2+)์์ ํน์ ์๋๋ง |
์ฑ ์ ์ฉ truststore (javax.net.ssl.trustStore) |
๊ทธ JVM ํ๋ก์ธ์ค | ๋ฐฐ์นยท๋จ๋ ์ฑ |
cacerts |
๊ทธ JDK๋ฅผ ์ฐ๋ ๋ชจ๋ JVM | ์ฌ๋ด ๋ฃจํธ์ฒ๋ผ ๋ชจ๋๊ฐ ์ ๋ขฐํด์ผ ํ ๋ |
| OS ์์คํ ํธ๋ฌ์คํธ | ๊ทธ ์๋ฒ์ JavaยทOpenSSL ๋ฑ | ์ฌ๋ด ๋ฃจํธ + ๋ฐฐํฌํ OpenJDK |
์ฑ ์ ์ฉ truststore๋ ์ด๋ ๊ฒ ๋ง๋ ๋ค.
keytool -importcert -alias partner-ca -file partner-ca.crt \
-keystore app-truststore.p12 -storetype PKCS12 -storepass:env TRUSTSTORE_PASS
-storepass:env๋ ํ๊ฒฝ๋ณ์์์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ฝ๋๋ค(:file์ ํ์ผ์์). ์
ธ ํ์คํ ๋ฆฌ์ ๋น๋ฐ๋ฒํธ๊ฐ ๋จ์ง
์๋๋ค. ๋น๋ฐ๋ฒํธ๋ 6์ ์ด์์ด์ด์ผ ํ๋ค.
cacerts์ ๋ฃ์ ๋๋ JDK ๋ฒ์ ์ ๋ฐ๋ผ ๋ช
๋ น์ด ๋ค๋ฅด๋ค.
# JDK 9 ์ด์
keytool -importcert -cacerts -alias corp-root-ca -file corp-root-ca.crt -storepass changeit
# JDK 8 (-cacerts ์ต์
์์)
keytool -importcert -trustcacerts -alias corp-root-ca -file corp-root-ca.crt \
-keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit
๋ ๋ช
๋ น ๋ชจ๋ -noprompt๋ฅผ ๋ถ์ด์ง ์์๋ค. keytool์ ์ํฌํธํ๊ธฐ ์ ์ ์ ๋ขฐ ๊ฒฝ๋ก๋ฅผ ๋ง๋ค์ด
๋ณด๊ณ (-trustcacerts๋ฅผ ์ฃผ๋ฉด cacerts๋ ๊ณ ๋ ค), ๋ชป ๋ง๋ค๋ฉด ์ง๋ฌธ์ ๋ณด์ฌ ์ฃผ๋ฉฐ ํ์ธ์ ๋ฌป๋๋ค. -noprompt๋ ์ด
ํ์ธ์ ์๋ตํ๋ค. ์คํฌ๋ฆฝํธ๋ฅผ ํธํ๊ฒ ํ๋ ค๊ณ ์ด ํ์ธ์ ๊ฑด๋๋ฐ์ง ์๋๋ค.
์์ธ 3. ์ค๋๋ JDK์ cacerts
์๋ฒ๋ ๊ณต๊ฐ CA ์ธ์ฆ์๋ฅผ ์ฐ๊ณ ์ฒด์ธ๋ ์จ์ ํ๋ฐ ํน์ ์๋ฒ์ Java๋ง ์คํจํ๋ค๋ฉด ๊ทธ JDK์ cacerts๋ฅผ
์์ฌํ๋ค.
์ ๋ฃจํธ๋ JDK ์
๋ฐ์ดํธ๋ก cacerts์ ๋ค์ด์จ๋ค. ์
๋ฐ์ดํธ๊ฐ ๋ฉ์ถ JDK๋ ๊ทธ ๋ค์ ๋์จ ๋ฃจํธ๋ฅผ ๋ชจ๋ฅธ๋ค. ์๋๊ฐ
์ ๋ฃจํธ ์ฒด์ธ์ ์ธ์ฆ์๋ก ๋ฐ๊พธ๋ ์๊ฐ ์ค๋๋ JDK๋ง ๋๊ธด๋ค.
Oracle ๋ฆด๋ฆฌ์ค ๋ ธํธ์ ์๋ค.
| ์ถ๊ฐ๋ ๋ฃจํธ | alias | ์ถ๊ฐ๋ ๋ฒ์ |
|---|---|---|
| Letโs Encrypt ISRG Root X2 (JDK-8317374) | letsencryptisrgx2 |
8u401, 17.0.10, 21.0.2 (2024-01-16) |
| WISeKey Global Root GBยทGC CA (JDK-8372351) | wisekeyglobalrootgbca, wisekeyglobalrootgcca |
21.0.12 (2026-07-21) |
๋จผ์ ์ฑ์ด ์ค์ ๋ก ์ฐ๋ JDK๋ฅผ ํ์ธํ๋ค. ์
ธ์ java์ Tomcat์ด๋ ์๋น์ค๊ฐ ์ฐ๋ java๊ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ๊ฐ ํํ๋ค.
ps -ef | grep [j]ava # ์คํ ์ค์ธ java ๋ฐ์ด๋๋ฆฌ ๊ฒฝ๋ก
/path/to/java -XshowSettings:properties -version 2>&1 | grep java.home
๊ทธ๋ค์ ๊ทธ JDK์ cacerts์ ํ์ํ ๋ฃจํธ๊ฐ ์๋์ง ๋ณธ๋ค. alias๊ฐ ์๋๋ผ ์ฃผ์ฒด ์ด๋ฆ์ผ๋ก ์ฐพ๋๋ค. OpenJDK
๋น๋์ cacerts๋ alias ๋ค์ [jdk] ์ ๋ฏธ์ฌ๋ฅผ ๋ถ์ด๊ณ (letsencryptisrgx2 [jdk]), ๋ฐฐํฌํ OpenJDK๋ alias
์ฒด๊ณ๊ฐ ์์ ๋ค๋ฅด๋ค. ๋ฆด๋ฆฌ์ค ๋
ธํธ์ alias๋ก -alias ์กฐํ๋ฅผ ํ๋ฉด ๋ฃจํธ๊ฐ ์์ด๋ โdoes not existโ๊ฐ ๋์ฌ ์
์๋ค.
# JDK 9 ์ด์
keytool -list -v -cacerts -storepass changeit | grep -i "ISRG Root X2"
# JDK 8
keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit \
| grep -i "ISRG Root X2"
๋ ์ค๋๋ JDK๋ ์ฌ์ ์ด ๋ ๋์๋ค. OpenJDK ์์ค์ ๋ cacerts๋ JDK 10 ์ ๊น์ง ๋น์ด ์์๋ค. JEP 319๊ฐ
JDK 10์์ ๊ธฐ๋ณธ ๋ฃจํธ ์ธํธ๋ฅผ ๋ฃ์๋ค. ๋น๋ํ ์ชฝ์ด ๋ฐ๋ก ์ฑ์ฐ์ง ์์ ๊ตฌํ OpenJDK๋ ๊ณต๊ฐ CA๋ ์ ๋ขฐํ์ง
๋ชปํ๋ค.
๋ฃจํธ๋ฅผ ์์ผ๋ก ๋ฃ๊ธฐ๋ณด๋ค JDK๋ฅผ ์ ๋ฐ์ดํธํ๋ค. ๋ฃจํธ ์ถ๊ฐ์ distrust๋ JDK ์ ๋ฐ์ดํธ์ ์ผ๋ถ๋ค. ์์ผ๋ก ๋ฃจํธ ํ ์ฅ์ ๋ฃ์ผ๋ฉด ๊ทธ ํ ๊ฑด๋ง ํ๋ฆฌ๊ณ , ์ดํ์ ์ถ๊ฐ๋ ์ ๊ฑฐ ์กฐ์น๋ ๊ณ์ ๋ชป ๋ฐ๋๋ค.
์์ธ 4. TLS๋ฅผ ๊ฒ์ฌํ๋ ์ฌ๋ด ๋ณด์ ์ฅ๋น
์ฌ๋ด ๋ณด์ ๊ฒ์ดํธ์จ์ด๋ ํ๋ก์๊ฐ HTTPS๋ฅผ ๋ณตํธํํด ๊ฒ์ฌํ ๋ค ์๊ธฐ ์ชฝ ์ธ์ฆ์๋ก ๋ค์ ์ํธํํ๋ ๊ตฌ์กฐ๋ค. JVM์ด ๋ฐ๋ ์๋ฒ ์ธ์ฆ์๋ ์๋ ์๋ฒ์ ๊ฒ์ด ์๋๋ผ ์ฅ๋น๊ฐ ์๋ก ๋ง๋ ๊ฒ์ด๊ณ , ๊ทธ ๋ฐ๊ธ์๋ ์ฅ๋น์ ๋ฃจํธ๋ค.
์ฌ๋์ด ์ฐ๋ PC์ OS ์ ์ฅ์์๋ ๋ณดํต ๊ทธ ๋ฃจํธ๊ฐ ๋ฐฐํฌ๋ผ ์์ด์ ๋ธ๋ผ์ฐ์ ๋ ์กฐ์ฉํ๋ค. ํ์ง๋ง ์์คํ ์ ์ฅ์๋ฅผ ์ฐ์ง ์๋ ์ฑ์๋ ๋ฃจํธ๋ฅผ ๋ฐ๋ก ๋ฃ์ด์ผ ํ๊ณ , ๋ฃ์ง ์์ผ๋ฉด ์ฑ์ด ์ฐ๊ฒฐ์ ๊ฑฐ๋ถํ๋ค. ํ ๊ฒ์ดํธ์จ์ด ๋ฒค๋ ๋ฌธ์์ ๊ทธ๋๋ก ์ ํ ๋ด์ฉ์ด๋ค. Java๊ฐ ๊ทธ ๋ํ๋ค.
์ฆ์์ ์ด๋ ๋ค.
- ์ฌ๋ด๋ง PC์ ์๋ฒ์์๋ง ์คํจํ๊ณ ์ธ๋ถ๋ง์์๋ ๋๋ค
- ๋์ ์๋ฒ๋ฅผ ๊ฐ๋ฆฌ์ง ์๊ณ ์ธ๋ถ HTTPS ํธ์ถ์ด ์ ๋ถ ์คํจํ๋ค
- ์ง๋จ 2)์์ ๋ณธ ๋ฐ๊ธ์๊ฐ 1)๊ณผ ๋ค๋ฅด๋ค
์ฅ๋น์ ๋ฃจํธ CA๋ฅผ JVM์ด ์ ๋ขฐํ๊ฒ ๋ง๋ ๋ค. ๋ฃจํธ ํ์ผ์ ๋ณด์ ๋ด๋น์๊ฒ ๋ฐ๊ณ , ์ง๋ฌธ ๋์กฐ๋ ์์ธ 2์ ๊ฐ๋ค.
| ํ๊ฒฝ | ๋ฐฉ๋ฒ |
|---|---|
| ๋ชจ๋ OS ๊ณตํต | ํด๋น JDK์ cacerts์ ์ํฌํธ (์์ธ 2 ๋ช
๋ น) |
| Windows ๊ฐ๋ฐ PC | -Djavax.net.ssl.trustStoreType=Windows-ROOT๋ก OS ๋ฃจํธ ์ ์ฅ์ ์ฌ์ฉ |
| macOS | ํด๋น JDK์ cacerts์ ์ํฌํธ. KeychainStore-ROOT(JDK 23+)๋ Apple ๊ธฐ๋ณธ ๋ฃจํธ๋ง ๋ด๊ณ ์์ด ์ฌ๋ด ์ฅ๋น ๋ฃจํธ๊ฐ ์๋ค |
| Linux ์๋ฒ (๋ฐฐํฌํ ํจํค์ง OpenJDK) | ์์คํ
ํธ๋ฌ์คํธ์ ์ถ๊ฐ (์๋ cacerts ์ค๋ฌด ์ฐธ๊ณ ). tarballยท๋ฒค๋ JDK๋ ํด๋น JDK cacerts์ ์ํฌํธ |
| ์ปจํ ์ด๋ | ์ด๋ฏธ์ง ๋น๋ ๋๋ ๊ธฐ๋ ์ ๋ฃจํธ ์ฃผ์ (์๋ ์ฐธ๊ณ ) |
java -Djavax.net.ssl.trustStoreType=Windows-ROOT -jar app.jar
Windows-ROOT๋ ํ์ฌ ์ฌ์ฉ์์ ๋ฃจํธ ์ ์ฅ์๋ค(Windows-ROOT-CURRENTUSER์ ๊ฐ๋ค). ์๋น์ค ๊ณ์ ์ผ๋ก ๋๋
์ฑ์ด๋ฉด ๋ชจ๋ ๊ณ์ ์ด ๊ณต์ ํ๋ Windows-ROOT-LOCALMACHINE์ ์ด๋ค. ์ด ํ์
์ JDK 19์์ ์ถ๊ฐ๋ผ 17 ๋ฑ์๋
์
๋ฐ์ดํธ๋ก ๋ค์ด๊ฐ๊ณ , JDK 8 ๋ฌธ์์๋ ์๋ค. ์ฐ๋ JDK์ Providers ๋ฌธ์์ ์๋์ง ํ์ธํ๋ค.
ํน์ ๋๋ฉ์ธ์ ์ฅ๋น์ ๊ฒ์ฌ ๋์์์ ๋นผ๋(bypass) ๋ฐฉ๋ฒ๋ ์๋ค. ๋ณด์ ์ ์ฑ ์ ๊ฑธ๋ฆฐ ํ๋จ์ด๋ ์ฅ๋น ๋ด๋น๊ณผ ์ ํ๋ค. ํ๋ณ๊ณผ ์ฒ๋ฆฌ ๋ฐฉํฅ์ ERR_CERT_AUTHORITY_INVALID ์์ธ๋ณ ํด๊ฒฐ์ TLS ๊ฒ์ฌ ์ฅ๋น ํญ๋ชฉ๊ณผ ๊ฐ๋ค.
์์ธ 5. ์๋ชป ์ง์ ํ trustStore
์ฆ์์ ๋ ์ค ํ๋๋ค. ๊ณต๊ฐ CA๋ฅผ ์ฐ๋ ๋ฉ์ฉกํ API๊น์ง ํ๊บผ๋ฒ์ ์คํจํ๊ฑฐ๋, ์ฌ์ค CA๋ฅผ ๋ฃ์ truststore๋ฅผ ์ง์ ํ๋๋ฐ ์ฌ์ค CA ์๋ฒ๋ง ์ฌ์ ํ ์คํจํ๋ค. ์ด์ ๋ฐฐํฌ์์ JVM ์ต์ ์ ๊ฑด๋๋ ธ๋ค๋ฉด ์ด๊ฒ๋ถํฐ ๋ณธ๋ค.
JSSE์ ๊ธฐ๋ณธ TrustManagerFactory๋ ์ด ์์๋ก ์ ๋ขฐ ์ ์ฅ์๋ฅผ ์ฐพ๋๋ค.
javax.net.ssl.trustStore์์คํ ์์ฑjava-home/lib/security/jssecacertsjava-home/lib/security/cacerts
๋จผ์ ์ฐพ์ ํ๋๋ง ์ด๋ค. ํจ์ ์ ์ฌ๊ธฐ์ ๋์จ๋ค.
| ํจ์ | JSSE ๋์ | ๊ฒฐ๊ณผ |
|---|---|---|
| trustStore ๊ฒฝ๋ก ์คํยทํ์ผ ์์ | (ํํ JDK) ๊ฒฝ๊ณ ์์ด cacerts๋ก ๋์ฒด |
๊ณต๊ฐ ์๋ฒ๋ ๋๊ณ ์ฌ์ค CA ์๋ฒ๋ง PKIX ์ค๋ฅ |
| ์ฌ์ค CA ํ ์ฅ๋ง ๋ ํ์ผ์ ์ง์ | cacerts๋ฅผ ๋ณด์ถฉํ์ง ์๊ณ ๋์ฒด |
์ฌ์ค CA ์๋ฒ๋ง ๋๊ณ ๊ณต๊ฐ ์๋ฒ๋ ์ ๋ถ ์คํจ |
๋๊ตฐ๊ฐ ๋ง๋ค์ด ๋ jssecacerts |
cacerts๋ณด๋ค ๋จผ์ ์ฐ์ |
cacerts์ ๋ฃจํธ๋ฅผ ๋ฃ์ด๋ ๋ฐ์ ์ ๋จ |
๋น๋ฐ๋ฒํธ๋ฅผ ๊ฑธ์ด ๋ง๋ PKCS12์ trustStorePassword๋ฅผ ์ ์ค |
์ํธํ๋ ์ธ์ฆ์๋ฅผ ๊ฑด๋๋ฐ๊ณ ์ฝ์ | ์ ๋ขฐ ์ธ์ฆ์ 0๊ฐ, ์ฆ ๋น truststore |
์ฒซ ๋ฒ์งธ๊ฐ ๊ฐ์ฅ ํ๋ฌดํ๋ค. ๊ฒฝ๋ก ํ ๊ธ์ ์คํ์ธ๋ฐ ์๋ฌ ๋ฉ์์ง๋ โํ์ผ ์์โ์ด ์๋๋ผ, ๋ฃ์๋ค๊ณ ์๊ฐํ ์ฌ์ค
CA ์๋ฒ์ PKIX ์ค๋ฅ๋ค. Oracle JSSE ๊ฐ์ด๋์๋ โ๋น keystore๋ก ๋ง๋ ๋คโ๊ณ ์ ํ ์์ง๋ง, ํ์ฌ ์ ์ง๋ณด์ ์ค์ธ
OpenJDK ๊ตฌํ(TrustStoreManager)์ ์ง์ ํ ํ์ผ์ ์ฝ์ ์ ์์ผ๋ฉด cacerts๋ก ๋์ด๊ฐ๋ค. ์ง๋จ 3)์
trustmanager ์ถ๋ ฅ์ Inaccessible trust store: <๊ฒฝ๋ก>๊ฐ ์ฐํ๋ค.
truststore๊ฐ ์ ๋ง ๋น์ด ์์ผ๋ฉด(๋ค ๋ฒ์งธ ํจ์ ๋ฑ) ์๋ฌ๊ฐ PKIX๊ฐ ์๋๋ผ
the trustAnchors parameter must be non-empty๋ก ๋ฐ๋๋ค. ์ด ๋ฉ์์ง๊ฐ ๋ณด์ด๋ฉด trustStore ์ง์ ๋ถํฐ ๋ณธ๋ค.
ํ์
๊ณผ ๋น๋ฐ๋ฒํธ๋ ๋ณธ๋ค. javax.net.ssl.trustStoreType์ ๊ธฐ๋ณธ๊ฐ์ KeyStore.getDefaultType()์ด๊ณ ,
JDK 9๋ถํฐ ๊ธฐ๋ณธ keystore ํ์
์ด PKCS12๋ค. ํ์ผ ํ์๊ณผ ํท๊ฐ๋ฆฌ์ง ์๊ฒ ๋ ๋ช
์ํ๋ค.
javax.net.ssl.trustStorePassword๋ ๊ธฐ๋ณธ๊ฐ์ด ์๋ค. ์์ผ๋ฉด JSSE๋ ๋น๋ฐ๋ฒํธ ์์ด(null) ์ฝ๋๋ฐ, keytool์ด
๊ธฐ๋ณธ ์ค์ ์ผ๋ก ๋ง๋ PKCS12๋ ์ธ์ฆ์๋ฅผ ๋น๋ฐ๋ฒํธ๋ก ์ํธํํด ๋๊ธฐ ๋๋ฌธ์ ํ๋๋ ์ฝํ์ง ์๋๋ค.
์ด๋์ ์ง์ ๋๋์ง๋ ์ด๋ ๊ฒ ์ฐพ๋๋ค.
# ์คํ ์ค์ธ JVM ๋ช
๋ นํ (๋น๋ฐ๋ฒํธ ์์ฑ์ ๋นผ๊ณ ์ถ๋ ฅ)
ps -ef | grep [j]ava | grep -oE 'javax\.net\.ssl\.trustStore(Type)?=[^ ]*'
# Tomcat ๊ธฐ๋ ์คํฌ๋ฆฝํธ
grep -n trustStore "$CATALINA_BASE/bin/setenv.sh"
# jssecacerts ๊ฐ ์๋์ง (JDK 8 ์ $JAVA_HOME/jre/lib/security)
ls "$JAVA_HOME/lib/security/"
์ปจํ
์ด๋๋ผ๋ฉด ์ด๋ฏธ์ง๊ฐ JAVA_TOOL_OPTIONS๋ก trustStore๋ฅผ ๋ฐ๊ฟ ๋ผ์ฐ๋ ๊ฒฝ์ฐ๋ ์๋ค(Temurin ์ด๋ฏธ์ง๋ฅผ
non-root๋ก ๋๋ฆด ๋, ์๋ ์ฐธ๊ณ ). ๊ฐ์ฅ ํ์คํ ๊ฑด ์ง๋จ 3)์ trustmanager ์ถ๋ ฅ์ด๋ค.
์ ์ฉ truststore๋ฅผ ์ฐ๋ ค๋ฉด ๊ณต์ธ ๋ฃจํธ๋ ํ์ํ์ง๋ถํฐ ๋ฐ์ง๋ค. ๊ณต๊ฐ API๋ ๋ถ๋ฅด๋ ์ฑ์ด๋ฉด cacerts ์ฌ๋ณธ์ ์ฌ์ค
CA๋ฅผ ์ถ๊ฐํ๊ณ ๊ทธ ์ฌ๋ณธ์ ์ง์ ํ๋ค. ํน์ ์๋๋ง ์ฌ์ค CA๋ผ๋ฉด JVM ์ ์ญ ์ค์ ๋์ Spring Boot SSL Bundle๋ก ๊ทธ
ํด๋ผ์ด์ธํธ์๋ง ์ ์ฉํ๋ค.
cacerts ์ค๋ฌด
๊ฐ์ ๋ช ๋ น์ด JDK ๋ฒ์ ์ ๋ฐ๋ผ ์ ๋จน๋๋ค.
| ํญ๋ชฉ | JDK 8 | JDK 9 ์ด์ |
|---|---|---|
| ๊ฒฝ๋ก | $JAVA_HOME/jre/lib/security/cacerts |
$JAVA_HOME/lib/security/cacerts |
keytool -cacerts |
์์ (-keystore๋ก ๊ฒฝ๋ก ์ง์ ) |
์์ |
| ์ด๊ธฐ ๋น๋ฐ๋ฒํธ | changeit |
changeit |
| ํ์ผ ํ์ | JKS | JDK 17๊น์ง JKS, JDK 18๋ถํฐ ๋น๋ฐ๋ฒํธ ์๋ PKCS12 |
JDK 8์์๋ java.home์ด JDK ์์ jre ๋๋ ํฐ๋ฆฌ๋ผ์ ๊ฒฝ๋ก๊ฐ ๋ค๋ฅด๋ค. JDK 9(JEP 220)๋ถํฐ๋ JDK ์ด๋ฏธ์ง์
jre ํ์ ๋๋ ํฐ๋ฆฌ๊ฐ ์๋ค.
-cacerts๋ -keystore <cacerts ๊ฒฝ๋ก> -storetype <cacerts ํ์
>๊ณผ ๊ฐ๋ค. -keystore๋ -storetype๊ณผ
ํจ๊ป ์ฐ๋ฉด ์ค๋ฅ๊ฐ ๋๋ค. JDK 9์์ ์ถ๊ฐ๋๋ค(JDK-8162739).
JDK 18๋ถํฐ cacerts๋ ๋น๋ฐ๋ฒํธ ์๋ PKCS12๋ค. ์์ ์ธ์ฆ์๊ฐ ์ํธํ๋ผ ์์ง ์๊ณ ๋ฌด๊ฒฐ์ฑ์ฉ MacData๋ ์์ด์
์ด๋ค ๋น๋ฐ๋ฒํธ๋ก๋(์์ด๋) ์ฝํ๋ค. ๋ค๋ง JDK 21 keytool ๋ฌธ์์๋ ์ด๊ธฐ ๋น๋ฐ๋ฒํธ๊ฐ ์ฌ์ ํ changeit์ผ๋ก
์ ํ ์๋ค.
Oracle ๋ฌธ์๋ ์ค์น ํ cacerts ๋น๋ฐ๋ฒํธ์ ํ์ผ ์ ๊ทผ ๊ถํ์ ๋ฐ๊พธ๋ผ๊ณ ๊ถ๊ณ ํ๋ค.
๋ฐฐํฌํ OpenJDK๋ ์์คํ ์ด cacerts๋ฅผ ๋ง๋ ๋ค
Linux ๋ฐฐํฌํ ํจํค์ง๋ก ์ค์นํ OpenJDK๋ cacerts๋ฅผ ์์คํ
ํธ๋ฌ์คํธ๊ฐ ๋ง๋ ๋ค. keytool๋ก ์ง์ ๋ฃ์ผ๋ฉด ๋์ค์
์ฌ๋ผ์ง ์ ์๋ค.
RHEL ๊ณ์ด์ ๊ณต์ ์์คํ ์ธ์ฆ์ ์ ์ฅ์๋ฅผ NSS, GnuTLS, OpenSSL, Java๊ฐ ํจ๊ป ์ด๋ค.
sudo cp corp-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
Red Hat ์ง์๋ฒ ์ด์ค์๋ keytool๋ก cacerts์ ๋ฃ์ ์ธ์ฆ์๊ฐ ์
๋ฐ์ดํธ๋ฅผ ์ค์นํ ๋๋ง๋ค ๋ฎ์ด์จ์ง๋ค๋ ์ฌ๋ก๊ฐ
์ฌ๋ผ์ ์๋ค(๋ก๊ทธ์ธ์ด ํ์ํ ๋ฌธ์๋ผ ์ ๋ชฉ ๊ธฐ์ค์ผ๋ก๋ง ํ์ธํ๋ค). RHEL์์๋ ์ ๋ฐฉ๋ฒ์ด ์ ์์ด๋ค.
Debian๊ณผ Ubuntu์์๋ ca-certificates-java ํจํค์ง๊ฐ ca-certificates์ ํ
์ผ๋ก Java cacerts๋ฅผ
๊ฐฑ์ ํ๋ค.
# DER ๋ก ๋ฐ์๋ค๋ฉด ๋จผ์ PEM ์ผ๋ก ๋ณํํ๋ค
openssl x509 -inform der -in corp-root-ca.der -out corp-root-ca.crt
sudo cp corp-root-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
keytool๊ณผ ๋ฌ๋ฆฌ ์ฌ๊ธฐ์ ๋ฃ๋ ํ์ผ์ PEM ํ์์ด์ด์ผ ํ๋ค. -----BEGIN CERTIFICATE-----๋ก ์์ํ์ง ์์ผ๋ฉด
์์ฒ๋ผ ๋ณํํ ๋ค .crt ํ์ฅ์๋ก ์ ์ฅํ๋ค. ์ปจํ
์ด๋์ /certificates์ ๋ฃ๋ ํ์ผ๋ PEM์ผ๋ก ๋ง์ถ๋ค.
update-ca-certificates๋ /usr/local/share/ca-certificates ์๋์์ .crt ํ์ฅ์ ํ์ผ๋ง ํฌํจํ๊ณ ,
/etc/ca-certificates/update.d์ ํ
์ ์คํํ๋ค.
์ปจํ ์ด๋
์คํ ์ค์ธ ์ปจํ ์ด๋ ์์์ keytool๋ก ๋ฃ์ ์ธ์ฆ์๋ ์ปจํ ์ด๋๋ฅผ ์๋ก ๋ง๋ค๋ฉด ์ฌ๋ผ์ง๋ค. ์์ผ๋ก ๋ฃ๋ ๋ฐฉ์์ ํตํ์ง ์๋๋ค.
eclipse-temurin ์ด๋ฏธ์ง๋ /certificates์ ์ธ์ฆ์๋ฅผ ๋ง์ดํธํ๊ณ USE_SYSTEM_CA_CERTS๋ฅผ ์ค์ ํ๋ฉด JVM
truststore์ ์์คํ
CA ์ ์ฅ์ ์์ชฝ์ ์ถ๊ฐํด ์ค๋ค.
docker run -v $(pwd)/certs:/certificates/ -e USE_SYSTEM_CA_CERTS=1 eclipse-temurin:25
non-root๋ก ์คํํ๋ฉด cacerts๋ฅผ ๊ณ ์น ์ ์์ด์, ๋ณ๋ truststore๋ฅผ ๋ง๋ค๊ณ JAVA_TOOL_OPTIONS๋ฅผ ๋๋ ค JVM์ด
๊ทธ ํ์ผ์ ์ฐ๊ฒ ๋ฐ๊พผ๋ค. ์ด ์ํ์์ ์ฑ์ด ์๊ธฐ trustStore๋ฅผ ๋ ์ง์ ํ๋ฉด ์์ธ 5๊ฐ ๋๋ค. ๋ค๋ฅธ ๋ฒ ์ด์ค ์ด๋ฏธ์ง๋
๋น๋ ๋จ๊ณ์์ ๋ฃจํธ๋ฅผ ๋ฃ๋๋ค.
Tomcat, Spring Boot, ๋ฐฐ์น์์
Tomcat
์ฌ๊ธฐ์ Tomcat์ ์ธ๋ถ API๋ฅผ ํธ์ถํ๋ ์ชฝ์ด๋ค. Tomcat์ด HTTPS๋ฅผ ์ ๊ณตํ๋ ์ชฝ์ ์ธ์ฆ์ ์ค์ ์ Tomcat ๊ฐ์ด๋์ ์๋ค. ๋์ ํท๊ฐ๋ ค ์๋ฒ์ฉ keystore๋ฅผ ๋ง์ง๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
Tomcat ๋ฌธ์๋ JVM ์ต์
์ CATALINA_OPTS๋ก ์ฃผ๊ณ CATALINA_BASE/bin/setenv.sh(Windows๋ setenv.bat)์
๋๋ผ๊ณ ๊ถํ๋ค.
# $CATALINA_BASE/bin/setenv.sh
CATALINA_OPTS="$CATALINA_OPTS -Djavax.net.ssl.trustStore=/opt/tomcat/conf/app-truststore.p12 -Djavax.net.ssl.trustStoreType=PKCS12"
์ด ์์๋ ๋น๋ฐ๋ฒํธ ์๋ truststore๋ฅผ ์ ์ ๋ก ํ๋ค. ์์ธ 2์ ๋ช
๋ น์ฒ๋ผ -storepass๋ก ๋ง๋ PKCS12๋ฅผ ๊ทธ๋๋ก
์ง์ ํ๋ฉด trustStorePassword๊ฐ ์์ด์ ์ ๋ขฐ ์ธ์ฆ์๋ฅผ ํ๋๋ ์ฝ์ง ๋ชปํ๋ค(์์ธ 5์ ๋ค ๋ฒ์งธ ํจ์ ). ๋ ์ค
ํ๋๋ก ๋ง์ถ๋ค.
-Djavax.net.ssl.trustStorePassword๋ฅผ ์ถ๊ฐํ๋ค. ๋ช ๋ นํ์ ๋น๋ฐ๋ฒํธ๊ฐ ๋๋ฌ๋๋ ๋ฌธ์ ๋ ์๋ ๋ฐฐ์น ์ ์ ๋ณธ๋ค- ์ฒ์๋ถํฐ ๋น๋ฐ๋ฒํธ ์๋ truststore๋ก ๋ง๋ ๋ค(JDK 18
cacerts์ ๊ฐ์ ๋ฐฉ์, JDK 12+ keytool)
keytool -importcert -alias partner-ca -file partner-ca.crt \
-keystore app-truststore.p12 -storetype PKCS12 -storepass:env TRUSTSTORE_PASS \
-J-Dkeystore.pkcs12.certProtectionAlgorithm=NONE -J-Dkeystore.pkcs12.macAlgorithm=NONE
Spring Boot SSL Bundle
SSL Bundle์ Spring Boot 3.1์์ ๋ค์ด์๋ค. JKS๋ PEM ์ ๋ขฐ ์๋ฃ๋ฅผ ํ๋กํผํฐ๋ก ์ ์ํ๊ณ RestTemplate, WebClient ๋ฑ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ ์ฉํ๋ค. JVM ์ ์ญ์ ๊ฑด๋๋ฆฌ์ง ์๊ณ ํน์ ํด๋ผ์ด์ธํธ์๋ง ์ฌ์ค CA๋ฅผ ์ ๋ขฐ์ํฌ ์ ์์ด์, ํํธ๋ API ํ๋ ๋๋ฌธ์ ์๊ธด ๋ฌธ์ ๋ผ๋ฉด ์ด์ชฝ์ด ๊ฐ์ฅ ์ข์ ํด๋ฒ์ด๋ค.
PEM ํ์ผ๋ก:
spring.ssl.bundle.pem.partner.truststore.certificate=classpath:partner-ca.crt
keystore ํ์ผ๋ก:
spring.ssl.bundle.jks.partner.truststore.location=classpath:partner-truststore.p12
spring.ssl.bundle.jks.partner.truststore.password=${TRUSTSTORE_PASS}
spring.ssl.bundle.jks.partner.truststore.type=PKCS12
RestClient์ WebClient๋ ssl.fromBundle()์ applyํ๋ค. RestClient์ RestClientSsl์ Boot 3.2+์
์๋ค(3.1์์๋ RestTemplate๊ณผ WebClient๋ง).
@Bean
RestClient partnerClient(RestClient.Builder builder, RestClientSsl ssl) {
return builder.baseUrl("https://partner.example")
.apply(ssl.fromBundle("partner"))
.build();
}
RestTemplate์ Boot ๋ฒ์ ์ ๋ฐ๋ผ ๋ฉ์๋ ์ด๋ฆ์ด ๋ค๋ฅด๋ค.
restTemplateBuilder.setSslBundle(sslBundles.getBundle("partner")).build(); // Boot 3.1~3.3
restTemplateBuilder.sslBundle(sslBundles.getBundle("partner")).build(); // Boot 3.4+ (4.x ํฌํจ)
๋ฒ์ ๋ณ๋ก ๋ฌ๋ผ์ง๋ ๊ณณ์ ์ด๋ ๋ค.
| ํญ๋ชฉ | Boot 3.x | Boot 4.x |
|---|---|---|
| RestTemplateBuilder ๋ฉ์๋ | 3.1~3.3: setSslBundle(...) / 3.4+: sslBundle(...) (setSslBundle์ 3.4์์ deprecated) |
sslBundle(...) (setSslBundle ์ ๊ฑฐ) |
RestTemplateBuilder ํจํค์ง |
org.springframework.boot.web.client |
org.springframework.boot.restclient |
RestClientSsl ํจํค์ง |
org.springframework.boot.autoconfigure.web.client |
org.springframework.boot.restclient.autoconfigure |
| ํ๋กํผํฐ๋ก ๋ฒ๋ค ์ง์ | 3.4+: spring.http.client.ssl.bundle (3.3 ์ดํ๋ ์์) |
spring.http.clients.ssl.bundle |
์์ ๋ฅผ ๋ณต์ฌํ๋๋ฐ import๊ฐ ์ ๋ง์ผ๋ฉด ์ด ํจํค์ง ์ฐจ์ด๋ถํฐ ๋ณธ๋ค.
๋ฐฐ์น๋ ๋จ๋ JVM
java -Djavax.net.ssl.trustStore=/opt/app/app-truststore.p12 \
-Djavax.net.ssl.trustStoreType=PKCS12 \
-Djavax.net.ssl.trustStorePassword=... \
-jar batch.jar
์ด ํ์ผ์ cacerts๋ฅผ ๋์ฒดํ๋ค(์์ธ 5). ๊ณต๊ฐ API๋ ๋ถ๋ฅธ๋ค๋ฉด cacerts ์ฌ๋ณธ์ ์ถ๊ฐํ ํ์ผ์ ์ง์ ํ๋ค.
JSSE ๊ฐ์ด๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ช
๋ นํ์ฒ๋ผ ๋ค๋ฅธ ์ฌ์ฉ์๊ฐ ๋ณผ ์ ์๋ ๊ณณ์ ๋์ง ๋ง๋ผ๊ณ ํ๋ค. ๋ช
๋ นํ์ ps๋ก
๋ณด์ธ๋ค. ๋น๋ฐ๋ฒํธ๊ฐ ํ์ํ truststore๋ผ๋ฉด ์ฑ์ด ์ํฌ๋ฆฟ ์ ์ฅ์๋ ์ค์ ์์ ๊ฐ์ ์ฝ์ด ์ฒซ HTTPS ํธ์ถ ์ ์
์์ฑ์ผ๋ก ๋ฃ๊ฒ ํ๋ค. Spring Boot๋ผ๋ฉด SSL Bundle์ password๋ฅผ ํ๊ฒฝ๋ณ์๋ก ๋ฐ๋๋ค.
๋ฐ๊พผ ๋ค์๋ ์ฌ์์
cacerts๋ truststore๋ฅผ ๋ฐ๊พผ ๋ค์๋ ์คํ ์ค์ธ Java๋ฅผ ์ฌ์์ํด์ผ ๋ฐ์๋๋ค(TLS ๊ฒ์ฌ ๊ฒ์ดํธ์จ์ด ๋ฒค๋
๋ฌธ์). ๋ฃ์๋๋ฐ ์ ๋๋ค๋ฉด ์ฌ์์ํ๋์ง๋ถํฐ ํ์ธํ๋ค.
๊ฒ์ฆ์ ๋ ์ฝ๋๊ฐ ๋ณด์ด๋ฉด
๋ชจ๋ ์ธ์ฆ์๋ฅผ ๋ฐ์๋ค์ด๋ TrustManager๋ ํธ์คํธ ์ด๋ฆ ๊ฒ์ฆ ์๋ต์ ๊ฒ์ ๊ฒฐ๊ณผ์ ์์ฃผ ๋ณด์ด์ง๋ง ์์ธ์ ๊ฐ๋ฆด ๋ฟ์ด๋ค. CWE-295(๋ถ์ ์ ํ ์ธ์ฆ์ ๊ฒ์ฆ)์ ํด๋นํ๊ณ , ๊ณต๊ฒฉ์๊ฐ ํต์ ๊ฒฝ๋ก์ ๋ผ์ด๋ค์ด ์ ๋ขฐ๋ ์๋๋ฅผ ์์ฅํ ์ ์๊ฒ ๋๋ค. ์๋ฌ๋ ์ฌ๋ผ์ง์ง๋ง TLS๋ฅผ ์ฐ๋ ์ด์ ๋ ๊ฐ์ด ์ฌ๋ผ์ง๋ค.
์ด๋ฐ ์ฝ๋๊ฐ ์์ผ๋ฉด ๋๊ตฐ๊ฐ ์ด ์๋ฌ๋ฅผ ๋ฎ์ ํ์ ์ด๋ค.
// ๊ฒ์ฆํ์ง ์๋ TrustManager โ ์ง์ฐ๊ณ ์์ธ์ ์ฐพ๋๋ค
public void checkServerTrusted(X509Certificate[] chain, String authType) { }
์ฐธ์กฐ
- OpenJDK: PKIXValidator.java
- OpenJDK: SunCertPathBuilder.java
- OpenJDK 21u: TrustStoreManager.java
- OpenJDK: PKCS12KeyStore.java
- OpenJDK: GenerateCacerts.java (cacerts alias ์ ๋ฏธ์ฌ)
- Java SE 21: JSSE Reference Guide
- Java SE 21: Troubleshooting Security
- Java SE 21: Java PKI Programmerโs Guide
- Java SE 21: keytool
- Java SE 9: keytool
- Java SE 8: keytool
- JDK-8162739: Create new keytool option to access cacerts file
- JEP 220: Modular Run-Time Images
- JEP 319: Root Certificates
- JDK 18 Release Notes: cacerts ๋น๋ฐ๋ฒํธ ์๋ PKCS12 ์ ํ
- JDK 8u401 Release Notes: ISRG Root X2 ์ถ๊ฐ
- JDK 21.0.2 Release Notes
- JDK 21 Release Notes (์ ์ฒด): AIA ํํฐ, distrust, ๋ฃจํธ ์ถ๊ฐ
- Java SE 21: JDK Providers (SunMSCAPI)
- JDK 19 Release Notes: Windows ๋ก์ปฌ ๋จธ์ ํค์คํ ์ด
- Java SE 25: JDK Providers (Apple KeychainStore-ROOT)
- JDK-8321045: Load anchor certificates from Keychain keystore (KeychainStore-ROOT, JDK 23)
- OpenJDK: KeystoreImpl.m (KeychainStore-ROOT๊ฐ ์ฌ๋ SystemRootCertificates.keychain)
- RFC 8446 ยง4.4.2: Certificate
- nginx: ssl_certificate
- OpenSSL 3.0: openssl-s_client
- RHEL 9: Using shared system certificates
- Red Hat ์ง์๋ฒ ์ด์ค: RHEL์์ Java์ฉ ์ธ์ฆ์๊ฐ ์ ๋ฐ์ดํธ ๋ ๋ฎ์ด์จ์ง์ง ์๊ฒ ๋ฃ๋ ๋ฒ (๋ก๊ทธ์ธ ํ์)
- Debian: update-ca-certificates(8)
- Debian: ca-certificates-java ํจํค์ง
- Ubuntu Server: Install a root CA certificate in the trust store
- Docker Official Images: eclipse-temurin
- TLS ๊ฒ์ฌ ๊ฒ์ดํธ์จ์ด ๋ฒค๋ ๋ฌธ์: ์ฌ์ฉ์ ์ธก ์ธ์ฆ์ ์๋ ๋ฐฐํฌ
- Apache Tomcat 10.1: RUNNING.txt
- Spring Boot 3.1 Release Notes
- Spring Boot: SSL
- Spring Boot 3.3: Calling REST Services
- Spring Boot: Calling REST Services
- Spring Boot 3.4 API: RestTemplateBuilder (sslBundle, setSslBundle deprecated)
- Spring Boot 3.4: HttpClientProperties.java (spring.http.client.ssl.bundle)
- Spring Boot 3.3 API: RestClientSsl (Since 3.2.0)
- CWE-295: Improper Certificate Validation