์ˆ˜์ • ์•ฝ 35๋ถ„

Java์—์„œ PKIX path building failed๊ฐ€ ๋‚  ๋•Œ

์ด ๊ธ€์˜ ์ˆœ์„œ 11๊ฐœ ์ ˆ
  1. ๋ฉ”์‹œ์ง€ ์ฝ๊ธฐ
  2. ์ง„๋‹จ ์ˆœ์„œ
  3. ์›์ธ 1. ์ƒ๋Œ€ ์„œ๋ฒ„๊ฐ€ ์ค‘๊ฐ„ ์ธ์ฆ์„œ๋ฅผ ์•ˆ ๋ณด๋‚ธ๋‹ค
  4. ์›์ธ 2. ์‚ฌ์„ค CA๋‚˜ ์ž์ฒด์„œ๋ช… ์ธ์ฆ์„œ
  5. ์›์ธ 3. ์˜ค๋ž˜๋œ JDK์˜ cacerts
  6. ์›์ธ 4. TLS๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ์‚ฌ๋‚ด ๋ณด์•ˆ ์žฅ๋น„
  7. ์›์ธ 5. ์ž˜๋ชป ์ง€์ •ํ•œ trustStore
  8. cacerts ์‹ค๋ฌด
  9. Tomcat, Spring Boot, ๋ฐฐ์น˜์—์„œ
  10. ๊ฒ€์ฆ์„ ๋ˆ ์ฝ”๋“œ๊ฐ€ ๋ณด์ด๋ฉด
  11. ์ฐธ์กฐ

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 ํ•˜์œ„ ์˜ต์…˜: defaultctx handshake keygen keymanager pluggability record respmgr session sessioncache sslctx trustmanager
  • handshake๋Š” 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๋Š” ์ด ์ˆœ์„œ๋กœ ์‹ ๋ขฐ ์ €์žฅ์†Œ๋ฅผ ์ฐพ๋Š”๋‹ค.

  1. javax.net.ssl.trustStore ์‹œ์Šคํ…œ ์†์„ฑ
  2. java-home/lib/security/jssecacerts
  3. java-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) { }

์ฐธ์กฐ