[Java] SSL/TLS 인증서 가이드 - 환경별 적용과 트러블슈팅
0️⃣들어가며,
사내 서비스들의 SSL/TLS 인증서 갱신 작업을 진행하면서 생각보다 많은 시행착오를 겪었습니다.
대상 서비스들은 모두 Java 기반이었지만, 실행 환경은 내장 Tomcat · Nginx · Jetty로 다양했고 Java 8 레거시부터 Java 21까지 혼재되어 있었습니다. 인증서 자체는 동일해도 환경마다 요구하는 파일 형식과 설정 방식이 달랐고, 그 과정에서 평소에는 깊게 들여다보지 않았던 개념들을 다시 마주하게 되었습니다.
특히 두 가지 사고가 인상적이었습니다.
- 브라우저에서는 멀쩡한데 Java 클라이언트에서만 SSL 오류가 발생한 케이스 — 풀체인 인증서 누락이 원인이었지만, 브라우저의 AIA 자동 보완 기능 때문에 한참 동안 원인을 찾지 못했습니다.
- Tomcat이 정상 기동된 것처럼 보이지만 HTTPS가 전혀 동작하지 않은 케이스 — 개인키의 PBE 암호화 알고리즘이 Java 8과 호환되지 않는 문제였는데, keytool로는 이 차이가 보이지 않아 원인 추적이 어려웠습니다.
이 글에서는 인증서의 기본 구조와 환경별 적용 방법을 정리하고, 위 두 가지 트러블슈팅 사례를 함께 공유합니다. 비슷한 작업을 앞두고 있는 분들께 참고가 되었으면 합니다.
1️⃣인증서란
웹 서비스나 시스템 개발을 하다 보면 꼭 마주치게 되는 인증서(주로 SSL/TLS 인증서)는 주로 서비스 사이에 안전한 통신(https)을 위해 사용됩니다.
*️⃣인증서의 목적
인증서는 인터넷이라는 공개된 위험한 도로에서 안전하게 데이터를 주고받기 위한 '암호화된 신분증'입니다. 크게 3가지 이유 때문에 필수적으로 사용됩니다.
- 기밀성 (Encryption): 클라이언트와 서버가 주고받는 데이터를 암호화하여, 중간에 해커가 데이터를 가로채더라도 내용을 알 수 없게 만듭니다. (HTTP를 HTTPS로 만드는 핵심)
- 무결성 (Integrity): 데이터가 이동하는 중에 누군가 변조하지 않았음을 보장합니다.
- 신원 증명 (Authentication): 클라이언트가 접속한 사이트가 진짜 그 사이트가 맞는지 보장합니다. (예: 내가 접속한 곳이 네이버가 맞는지, 네이버를 흉내 낸 피싱 사이트가 아닌지 확인)
*️⃣인증서 기반 통신 아키텍처
인증서 아키텍처를 이해하려면 두 가지 개념을 알아야 합니다.
- CA (Certificate Authority, 인증기관): DigiCert, Let's Encrypt처럼 "이 서버는 진짜 이 도메인의 주인이 맞다"고 보증해 주는 공인된 제3의 기관입니다.
- 비대칭키 (공개키/개인키): 암호화할 때 쓰는 키(공개키)와 복호화할 때 쓰는 키(개인키)가 쌍으로 존재합니다.
비대칭키의 경우에는 암호화 알고리즘이 사용되며, RSA가 대표적으로 널리 사용되고 있습니다.
#️⃣Architecture
[브라우저/클라이언트] [웹 서버 (Nginx/Tomcat 등)]
│ │
│ 1. 접속 요청 (Client Hello) │
├──────────────────────────────────────────────────────────>│
│ │
│ 2. 서버 인증서 전달 (Server Hello + .crt) │
│ - 서버의 공개키가 들어있음 │
│<──────────────────────────────────────────────────────────┤
│ │
│ 3. 인증서 검증 및 대칭키 전달 │
│ - 브라우저에 내장된 CA 리스트로 검증 │
│ - 데이터를 암호화할 '비밀키'를 생성 후 │
│ 서버의 공개키로 암호화하여 전송 │
├──────────────────────────────────────────────────────────>│
│ │
│ 4. 서버 개인키(.key)로 │
│ 비밀키 복호화 │
│ │
│ 5. 이후 이 '비밀키'로 모든 데이터 암호화 통신 (HTTPS) │
│<─────────────────────────────────────────────────────────>│
서버 측
.crt(인증서)와 .key(개인키) 파일이 서버(Nginx, Apache 등)에 설정되어 있어야 합니다.
웹 브라우저
네이버나 구글에 접속할 때 일어나는 방식이며, 대개 단방향 SSL(One-way SSL) 아키텍처를 사용합니다. 즉, 클라이언트가 서버의 신원만 확인합니다.
Chrome, Safari 같은 브라우저나 OS가 세상의 유명한 CA(인증기관)들의 공개키를 이미 가지고 있습니다. 그래서 서버가 준 인증서가 진짜인지 바로 검증할 수 있습니다.
개발 환경(애플리케이션)
코드를 통해 프로그래밍 방식으로 다른 서버의 API를 호출하거나 내부적으로 통신하는 환경입니다. 런타임 환경의 인증서 저장소를 따릅니다.
Python의 경우 certifi라는 라이브러리가 자체 공인 CA 인증서 모음을 관리합니다.
Java, 내장 Tomcat(Spring Boot 등)의 경우 JVM에서 cacerts 파일과 jssecacerts 파일로 관리합니다.
cacerts (Default): Java가 기본적으로 제공하는 전 세계 공인 CA 인증서 모음집
jssecacerts (Custom): 개발자나 시스템 관리자가 우리 시스템 전용으로 추가한 인증서들을 격리해서 관리하는 공간. (초기 JDK 설치 시 파일 없음)
주의점
내부망(사설망)에서 통신할 때 공인 CA가 아닌 사설 CA로 인증서를 끊는 경우가 많습니다. 이 경우, 요청을 보내는 우리 시스템(클라이언트)에 "이 사설 CA는 안전한 기관이야"라고 인증서(TrustStore)를 수동으로 등록해 주어야 통신(SSL Handshake Exception 방지)이 가능합니다.
2️⃣인증서의 구조 이해
*️⃣CA 계층 구조
SSL 인증서는 단독으로 존재하지 않습니다. 신뢰 체계는 아래와 같이 계층 구조로 이루어져 있습니다.
루트 CA (Root CA)
└── 중간 CA (Intermediate CA) //1개 이상
└── 서버 인증서 (*.dev.com)
- 루트 CA: 최상위 인증 기관. 브라우저와 JVM에 이미 내장.
- 중간 CA: 루트 CA가 직접 서버 인증서를 발급하지 않고 중간 단계를 가집니다. ※보안 사고 시 루트 CA를 보호하기 위한 구조
- 서버 인증서: 실제 도메인에 발급된 인증서 ※와일드카드 등
*️⃣풀체인(Fullchain)이란?
클라이언트(브라우저, Java 등)는 서버 인증서를 받았을 때 이 인증서가 신뢰할 수 있는 루트 CA까지 연결되는지 검증합니다.
루트 CA는 이미 내장되어 있어서 알 수 있지만, 중간 CA 정보는 서버가 직접 전달해줘야 합니다.
중간 CA 없이 서버 인증서만 내려주면 클라이언트는 신뢰 체인을 완성할 수 없어 오류가 발생합니다.
즉, 풀체인 = 서버 인증서 + 중간 CA 를 하나의 파일로 합친 형태입니다.
루트 CA는 풀체인에 포함하지 않는다. 클라이언트에 이미 내장되어 있고, 일부 환경에서 포함 시 오류가 발생하는 케이스가 있다.
#️⃣생성 방법
// Linux
cat certificate.crt intermediate_ca.crt > fullchain.crt
// Windows (cmd)
copy /b certificate.crt + intermediate_ca.crt fullchain.crt
// 순서 중요: 서버 인증서가 먼저, 중간 CA가 뒤에 와야 한다.
*️⃣인증서 파일 형식
인증서 파일 형식(확장자)은 .crt .pem .jks 등 다양합니다.
✅.crt
CA(Certificate Authority, 인증기관)이 서명해준 인증서 파일입니다.
공개키 + 신원정보 + CA 서명만 들어가 있으며, 개인키는 없습니다.
Linux/Apache/Nginx 환경에서 주로 쓰는 확장자입니다. Windows에서는 같은 내용을 .cer로 부르기도 하며, 실질적으로 .crt와 .cer은 동일하다고 생각하면 됩니다. (주로 PEM 형식이지만 간혹 바이너리 형태인 DER 형식일 수도 있습니다.)
✅.key
.crt와 함께 사용되는 개인키 파일입니다.
Nginx의 경우 ssl_certificate_key옵션으로 지정하여 사용합니다.
✅.p12 | .pfx
개인키 + 인증서(풀체인)를 하나로 묶은 컨테이너입니다. 바이너리 포맷(PKCS#12 표준)입니다.
.p12와 .pfx는 이름만 다르고 동일한 포맷입니다.
.pfx는 Windows/IIS에서 주로 쓰던 명칭이고, .p12는 그 외 환경에서 사용됩니다.
비밀번호로 전체를 암호화할 수 있어서 파일 하나만 넘겨도 안전하게 키+인증서를 전달할 수 있습니다.
✅.jks + 아래 트러블 슈팅 2 참고
Java KeyStore의 약자입니다. Java가 자체적으로 만든 독자 포맷이며, .p12랑 역할은 같습니다. (개인키 + 인증서 형태)
차이는 Java 전용이라는 점입니다. JDK keytool로만 제대로 다룰 수 있고, openssl툴로는 직접 읽지 못합니다.
Java 9 이후로 Oracle이 공식적으로 .p12를 권장하기 시작해서 요즘 신규 프로젝트는 .p12로 가는 추세입니다.
.jks는 레거시 확장자이며, 뒤에 나올 트러블 슈팅도 해당 확장자로 인해 발생한 문제였습니다.
✅.pem
엄밀히 말하면 특정 확장자가 아니라 텍스트 기반의 인코딩 방식을 의미합니다.
실제로 우리가 쓰는 .crt, .key 파일의 내부 내용물은 대부분 이 PEM 인코딩(메모장으로 열었을 때 -----BEGIN...으로 시작하는 형태)으로 되어 있습니다.
따라서 외부에서 .pem 확장자로 파일을 받았다면, 그 안에 인증서가 들었는지 개인키가 들었는지(혹은 둘 다 들었는지)는 파일을 열어 내부 태그를 직접 확인해봐야 알 수 있습니다. (참고: PEM과 대비되는 바이너리 형태의 인코딩 방식은 .der 형식이 있습니다.)
//.pem 메모장 내용
-----BEGIN CERTIFICATE----- → 인증서
-----BEGIN PRIVATE KEY----- → 개인키
-----BEGIN CERTIFICATE REQUEST----- → CSR
✅서버 별 확장자
Apache 서버 : .crt, .pem 확장자 적용 가능
Nginx 서버 : .crt, .pem 확장자 적용 가능
Webtob 서버 : .crt, .pem 확장자 적용 가능
IIS 서버 : .pfx 확장자 적용 가능
Exchange 서버 : .pfx 확장자 적용 가능
Tomcat 서버 : .jks 확장자 적용 가능
Resin 서버 : .jks 확장자 적용 가능
Jboss 서버 : .jks 확장자 적용 가능
*️⃣KeyStore와 인증서 관리 도구
앞서 설명드린 인증서 파일(.crt, .key, .p12 등)은 단순히 파일 형태일 뿐입니다.
실제 서비스에서는 이러한 파일을 생성·변환·조회·검증하기 위한 도구가 필요하며, 대표적으로 OpenSSL과 Java keytool을 사용합니다.
✅ KeyStore란?
KeyStore는 인증서와 개인키를 저장하는 저장소(Storage)를 의미합니다.
즉 p12와 .jks는 모두 KeyStore의 한 종류입니다.
KeyStore의 구조는 아래와 같습니다.
Keystore
└── Entry (Alias)
├── Certificate Chain
│ ├── Server Certificate
│ ├── Intermediate CA
│ └── Root CA (선택)
│
└── Private Key
└── 암호화되어 저장
✅ OpenSSL
OpenSSL은 가장 널리 사용되는 SSL/TLS 관리 도구입니다.
Linux 서버에서는 사실상 표준으로 사용됩니다.
Windows 운영체제에서는 기본적으로 설치가 되어있지 않습니다. git이 설치되어 있을 경우에는 git bash를 통해 명령어 사용이 가능합니다. (git 설치팩에 포함되어 있기 때문)
=== 주요 명령어 ===
# 인증서 내용 확인
openssl x509 -in certificate.crt -text -noout
# 개인키 확인
openssl rsa -in private.key -check
# 인증서와 개인키 일치 여부 확인
openssl x509 -noout -modulus -in certificate.crt | openssl md5
openssl rsa -noout -modulus -in private.key | openssl md5
# PKCS12 생성
openssl pkcs12 -export \
-out server.p12 \
-inkey private.key \
-in fullchain.crt
OpenSSL은 아래와 같은 작업을 수행할 수 있습니다.
- 인증서 조회
- CSR 생성
- 개인키 생성
- PKCS#12 생성
- 체인 검증
다만 Java 전용 형식인 .jks는 직접 다룰 수 없습니다.
✅ keytool
keytool은 JDK에 포함된 Java 전용 인증서 관리 도구입니다.
JDK를 설치하면 기본 제공되며, JDK 버전에 따른 차이가 있을 수 있습니다.
=== 주요 명령어 ===
# KeyStore 조회
keytool -list -v -keystore server.jks
# 인증서 Import
keytool -importcert
# JKS 생성
keytool -genkeypair
# PKCS12 ↔ JKS 변환
keytool -importkeystore
Java 애플리케이션(Tomcat, Spring Boot, Jetty 등)은 일반적으로 keytool로 생성한 KeyStore를 사용합니다.
✅ Portecle
Portecle는 KeyStore를 GUI 환경에서 관리할 수 있는 Java 기반 오픈소스 도구입니다.
keytool 명령어가 익숙하지 않은 경우 시각적으로 KeyStore 내부를 확인하고 조작할 수 있어 유용합니다.
공식 배포 페이지: https://portecle.sourceforge.net
주요 기능으로는 아래와 같습니다.
- KeyStore 파일(.jks, .p12) 내용 조회
- 인증서 체인 시각적 확인
- alias 조회/변경
- 인증서 Import/Export
- KeyStore 타입 변환 (JKS <-> PKCS12)
- 비밀번호 변경
GUI 기반의 시각적 확인이 매력적인 툴입니다.
3️⃣ 인증서 갱신 작업
인증서 갱신 작업은 서비스의 HTTPS 연결을 누가(Nginx, Tomcat) 담당하느냐에 따라 다릅니다.
#️⃣Nginx
Nginx 웹서버의 경우 인증서를 적용/갱신하는 것은 비교적 간단히 파일 교체와 설정 변경 후 재시작하는 것으로 가능합니다.
자세한 설정은 아래 포스팅을 참고해주세요.
[EC2] Nginx - SSL 인증서 적용
#️⃣Application
JVM 기반의 애플리케이션 인증 계층은 아래와 같이 구성됩니다.
Application(SpringBoot/Tomcat/Jetty)
↓
JSSE(Java SSL/TLS)
↓
JVM
애플리케이션은 JVM(JSSE)를 이용하여 HTTPS 연결을 처리하며, 이 과정에서 서비스마다 KeyStore 설정을 하는 곳은 조금씩 다릅니다.
Spring Boot의 경우 .yaml파일에서 설정이 가능하며, Jenkins의 경우에는 jenkins.xml이라는 파일에서 KeyStore를 설정할 수 있습니다. 또한 대부분의 Tomcat 기반 서비스들은 server.xml 파일에서 관리하는 경우가 많습니다.
인증서를 적용하기 위해 설정하는 옵션은 주로 아래와 같습니다.
1. HTTPS 포트 - port
2. KeyStore 경로 - KeystoreFile
3. KeyStore 타입 - KeystoreType
4. KeyStore 비밀번호 - KeystorePass
5. 인증서 Alias (선택) - keyAlias
상용 제품을 온프레미스로 운영하고 있는 환경에서는, 제품 자체에서 관리(소스코드 내부, 별도 설정 파일)되고 있는 케이스도 있기 때문에 공식 문서 혹은 벤더를 통해 확인하는 것이 좋습니다.
4️⃣트러블 슈팅 1: 인증서는 정상인데 REST API 호출만 실패한 이유
사내 웹서비스 Nginx의 인증서 갱신 이후 브라우저(Front, Swagger)에서는 정상 접속이 가능했지만, Java 기반 서비스 간 API 호출에서 SSL 오류가 발생하는 문제가 발생했습니다.
브라우저 정상 동작만 확인하는 바람에, API 호출에서 오류가 발생하는 것을 인지하는데 시간이 조금 걸려 다운타임이 길게 발생하는 문제가 있었습니다,,
#️⃣발생 증상
Jenkins 및 Codebeamer 툴 등에서 API서버(인증서 갱신한 서비스)로 호출 시 다음과 같은 오류가 발생했습니다.
//Groovy Built-in Http CLient Error
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
//Jenkins HttpRequest Error
HTTPSConnectionPool(host='api-almhub.sl.com', port=443): Max retries exceeded with url: /api/v1/analysis/misra (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
처음에는 위 오류에 대해 중간 CA가 각 서비스 cacert(신뢰 저장소) 목록에 없는 것으로 인지하였습니다.(LLM 답변을 통해)

Python 및 Java 버전 별로 CA를 주입하였고, 그 결과 정상적으로 API 호출이 되는 것을 확인했습니다.
그러나 생각해보니 서버의 인증서 API가 변경이 될 때마다, 해당 API를 사용하는 써드파티에서 갱신을 해주는 것은 미친 짓(❓)이 아닌가 하는 의심이 들었습니다..
#️⃣추가 조치
생각한 것처럼 미친 짓을 해 줄 필요는 없었고, Nginx 설정을 다시 확인해보니 서버 인증서만 주입하여 사용 중이였고 위에서 설명했던 풀체인 인증서를 생성하여 설정을 변경해주는 것으로 해결할 수 있었습니다.
이번 문제는 "브라우저에서는 정상 동작을 했다"라는 것에 의해 문제를 인지 + 조치 하는데까지 시간이 좀 걸린 것 같습니다.
추가로 브라우저에서는 왜 접속이 되었는지에 대해 알아보니,브라우저는 인증서의 AIA(Authority Information Access) 정보를 이용하여 누락된 중간 CA를 자동으로 다운로드하는 기능을 제공한다고 합니다.
반면 Java, curl, 일부 API Gateway 등은 이러한 자동 보완 기능을 수행하지 않아서 문제가 발생할 수 있다고 합니다.
따라서 브라우저에서는 문제가 보이지 않았지만 Java 클라이언트는 인증서 체인을 완성할 수 없어 SSL 검증에 실패했다고 볼 수 있습니다.
5️⃣트러블 슈팅 2: 개인키 암호화 알고리즘 차이로 인한 적용 오류
⏩문제 배경
대상 서비스는 상용 제품으로 구조 변경이 불가능한 레거시 환경(Java SE 1.8)에서 운영 중입니다. 인증서 갱신 주기가 돌아와 인증서 관리 부서로부터 새 .jks 파일을 수령하였고, 작년에 진행했던 매뉴얼을 참고하여 동일한 방식으로 KeyStore를 교체 후 서비스를 재시작하였습니다.
그러나 웹 서비스 접속이 전혀 되지 않는 현상이 발생했습니다.
증상
- 브라우저 접속 시 ERR_CONNECTION_TIMED_OUT (TIME_OUT_ERROR)
- Tomcat 프로세스는 정상적으로 기동된 것처럼 보임
- 서버 로그가 정상 기동 케이스와 동일하여 원인 추적 불가
KeyStore의 Alias, Password, Type은 기존과 동일하게 맞춰주었음에도 불구하고 동일 증상이 반복되었습니다.
⏩원인 추적
1단계: 로그만으로는 추적 불가
기본 로그 레벨에서는 SSL 초기화 실패가 출력되지 않아 Tomcat이 정상 기동된 것처럼 보였습니다. 따라서 로그 레벨을 상향 조정한 뒤 재기동하여 상세 로그를 확인했습니다.
제품의 공식 문서를 참고하여 .properties 옵션을 추가 적용하였습니다.
2단계: 상세 로그에서 오류 확인
로그 레벨 조정 후 아래 예외를 확인할 수 있었습니다.
Caused by: java.io.IOException: parseAlgParameters failed:
ObjectIdentifier() -- data isn't an object ID (tag = 48)
at sun.security.pkcs12.PKCS12KeyStore.parseAlgParameters
...
Caused by: java.io.IOException:
ObjectIdentifier() -- data isn't an object ID (tag = 48)
at com.sun.crypto.provider.PBES2Parameters.engineInit
parseAlgParameters failed — 알고리즘 파라미터 파싱 실패로그이며, 인증서 자체의 문제가 아니라 개인키의 암호화 알고리즘을 Java 8이 해석하지 못한 것으로 확인할 수 있었습니다.
3단계: 왜 keytool 조회에서는 문제가 안 보였는가
keytool -list -v -keystore server.jks -storepass 비밀번호
위 명령으로 확인되는 알고리즘은 인증서 서명 알고리즘만 출력됩니다.
Signature algorithm name: SHA256withRSA ← 기존과 동일하게 보임
SHA256withRSA는 기존 정상 동작하는 인증서와 동일했기 때문에 알고리즘 문제가 아닐 것이라 오판하기 쉽습니다.
문제는 인증서 서명 알고리즘이 아니라, 개인키를 보호하는 PBE(Password-Based Encryption) 알고리즘이며, 이 값은 keytool로는 확인이 불가능합니다.
.jks는 Oracle 독자 포맷이라 openssl로도 직접 조회할 수 없다. .jks → .p12 변환 후 openssl pkcs12 -info로 확인해야 한다.
4단계: .p12 변환 후 알고리즘 확인
# .jks → .p12 변환
keytool -importkeystore \
-srckeystore server.jks \
-destkeystore server.p12 \
-deststoretype PKCS12 \
-srcstorepass 비밀번호 \
-deststorepass 비밀번호
# 개인키 암호화 알고리즘 확인
openssl pkcs12 -info -in server.p12 -passin pass:비밀번호 -noout
=== 출력 결과: ===
# 새로 받은 인증서 (문제 파일)
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 10000, PRF hmacWithSHA256
# 기존 정상 동작 인증서
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC
이로써 원인을 명확하게 파악할 수 있었습니다.
새 인증서의 개인키가 AES-256-CBC로 암호화되어 있었고, Java 8u192(실제 운영 환경)는 이 PBES2 파라미터 구조를 해석하지 못하는 버그라고 특정지었습니다.
⏩원인 요약
| 항목 | 기존 인증서 | 새 인증서 |
| 개인키 PBE 알고리즘 | pbeWithSHA1And3-KeyTripleDES-CBC | PBES2 / AES-256-CBC |
| Java 8 호환 여부 | ✅ | ❌ |
| 인증서 서명 알고리즘 | SHA256withRSA | SHA256withRSA (동일) |
| keytool 조회 시 차이 | 없음 | 없음 (오판 원인) |
새로 받은 .jks는 최신 JDK 환경의 keytool로 생성되었으며(추정), Java 11 이후 keytool의 기본 PBE 알고리즘은 AES-256-CBC로 변경되었습니다. Java 8u192의 PKCS12 구현은 이 PBES2 파라미터 구조를 정상적으로 파싱하지 못해 SSL 초기화 단계에서 조용히 실패한 것으로 확인됩니다.
⏩조치
.crt와 .key 파일을 이용해 Java 8 호환 알고리즘으로 KeyStore를 직접 재생성하는 방식으로 진행할 수 있습니다.
openssl 3.x 버전도 기본 알고리즘이 AES-256이므로 -legacy 옵션을 명시해야 한다.
Step 1. Java 8 호환 PKCS12 생성
openssl pkcs12 -export \
-in certificate.crt \
-inkey domain.key \
-certfile intermediate_ca.crt \
-out server_legacy.p12 \
-passout pass:비밀번호 \
-keypbe PBE-SHA1-3DES \
-certpbe PBE-SHA1-3DES \
-macalg SHA1 \
-legacy
Step 2. PKCS12 → JKS 변환 (반드시 Java 8 keytool 사용)
$JAVA8_HOME/bin/keytool -importkeystore \
-srckeystore server_legacy.p12 \
-srcstoretype PKCS12 \
-destkeystore server_java8.jks \
-deststoretype JKS \
-srcstorepass 비밀번호 \
-deststorepass 비밀번호
주의: 이 단계도 반드시 Java 8 keytool을 사용해야 한다. Java 11+ keytool로 변환하면 JKS 포맷으로 저장하더라도 내부 개인키가 다시 AES-256으로 재암호화된다.
Step 3. 알고리즘 재확인
keytool -importkeystore \
-srckeystore server_java8.jks \
-destkeystore verify.p12 \
-deststoretype PKCS12 \
-srcstorepass 비밀번호 \
-deststorepass 비밀번호
openssl pkcs12 -info -in verify.p12 -passin pass:비밀번호 -noout
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC ← Java 8 호환 확인
⏩정리
이번 이슈의 핵심은 "정상 기동처럼 보이지만 SSL이 적용되지 않는" 조용한 실패였습니다.
기본 로그 레벨에서는 SSL 초기화 실패가 출력되지 않아 원인 추적이 어려웠으며, keytool -list로 확인되는 서명 알고리즘(SHA256withRSA)이 정상으로 보여 알고리즘 문제를 초기에 배제하는 오판을 유발했습니다.
인증서 교체 시 반드시 확인해야 할 사항:
| 확인 항목 | 방법 |
| 인증서 서명 알고리즘 | keytool -list -v |
| 개인키 PBE 알고리즘 | .jks → .p12 변환 후 openssl pkcs12 -info |
| Java 버전 호환 여부 | AES-256 → Java 8 비호환 / 3DES → Java 8 호환 |
| KeyStore 생성 도구 버전 | Java 8 환경이면 반드시 Java 8 keytool 사용 |
keytool로 보이는 알고리즘과 실제 개인키 암호화 알고리즘은 다르다. 레거시 Java 환경에서 인증서를 교체할 때는 개인키 PBE 알고리즘까지 반드시 확인해야 한다.
6️⃣Reference
트러블 슈팅 1
https://www.sslcert.co.kr/guides/kb/84
Java 기반에서 SSL 접속시, PKIX path building failed 오류 - SecureSign
Java 기반에서 SSL 접속시, PKIX path building failed 오류 - SecureSign
www.sslcert.co.kr
트러블 슈팅 2
https://bugs.openjdk.org/browse/JDK-8267837?attachmentViewMode=list
Loading...
bugs.openjdk.org
'DevOps.' 카테고리의 다른 글
| [Jenkins] 스프링부트 프로젝트 CICD 테스트 +삽질 로그 (0) | 2025.02.12 |
|---|---|
| [Jenkins] Item 추가 및 Pipeline 작성 + 테스트 (0) | 2025.02.12 |
| [Jenkins] Github 자격 증명 추가 + 웹훅 설정 (0) | 2025.02.12 |
| [Ubuntu] Java 및 Jenkins 설치 + 스왑 메모리 (2) | 2025.02.09 |
| 이게 CI야? CD야? (0) | 2025.02.09 |