Loading Engineering Log...

PKCS12(Java 8) 호환성 문제 SSL 인증서 갱신 회고

개요

최근 사내에서 운영 중인 PTC Integrity Server 12.x(Java 8 사용)의 SSL 인증서 만료가 임박하여 인증서 갱신 작업을 진행하였다.

작업 자체는 단순히 인증서를 교체하는 수준으로 예상했지만, 실제로는 여러 단계의 오류가 발생했고 최종적으로는 Java 8 기반 환경과 OpenSSL 3.x가 생성한 PKCS12 포맷 간의 호환성 문제를 확인하게 되었다.

이 글은 인증서 갱신 과정에서 발생한 문제와 원인을 분석하고, 왜 최신 OpenSSL 환경에서 생성한 PKCS12가 Java 8 기반 애플리케이션에서 문제가 될 수 있는지 정리해보려고 한다.


시스템 환경

PTC Integrity Server

PTC Integrity Server 12.x
Tomcat Embedded
Java 8 (JRE 1.8)
Windows Server

 

SSL 구성

Integrity는 일반적인 웹 애플리케이션과 달리 다음과 같은 SSL 저장소를 사용한다.

certificate.p12
cacerts
jssecacerts

각 저장소의 역할은 서로 다르다.


SSL 저장소 역할 이해하기

certificate.p12

서버 자신의 인증 정보를 저장하는 KeyStore

서버 인증서
개인키(Private Key)
인증서 체인

이 저장된다.

쉽게 말하면 서버의 신분증이다.


cacerts

Java 기본 TrustStore

어떤 인증기관(CA)을 신뢰할 것인가

를 정의한다.

예)

GlobalSign Root CA
DigiCert Root CA

등이 저장된다.


jssecacerts

Java SSL 전용 TrustStore

존재할 경우 Java는 cacerts보다 jssecacerts를 우선 사용한다.

역할은 cacerts와 동일하다.


PKCS12란?

정식 명칭은

PKCS#12
Public Key Cryptography Standards #12
 

입니다.

쉽게 말하면

인증서 + 개인키 + 인증서 체인을 하나의 파일로 묶어놓은 표준 포맷

입니다.


예를 들어 SSL 서버를 운영하려면 원래는 이런 파일들이 따로 있습니다.

*.domain.com.crt      ← 서버 인증서
private.key           ← 개인키
chain.crt             ← 중간 인증서
root.crt              ← 루트 인증서
 

이걸 하나로 묶은 게

certificate.p12
 

입니다.

처음 생각했던 문제

인증서 갱신 후 서버가 정상 기동되지 않았다.

처음에는 자연스럽게 다음과 같은 원인을 의심했다.

인증서 체인 오류
Root CA 등록 실패
Intermediate CA 등록 실패

하지만 실제 원인은 전혀 달랐다.


SSL 초기화 실패

이후 발생한 오류

Private key not stored as PKCS#8 EncryptedPrivateKeyInfo

java.security.UnrecoverableKeyException

ObjectIdentifier() -- data isn't an object ID

이 시점부터 단순 인증서 문제가 아니라는 것을 알 수 있었다.


로그 분석

실패 위치를 따라가 보면

SSLContextUtil.getKeyManagers()
↓
KeyStore.getKey()
↓
UnrecoverableKeyException

에서 예외가 발생한다.

중요한 점은

certificate.p12 읽기 성공
비밀번호 검증 성공

상태였다는 것이다.

즉,

파일 문제 아님
비밀번호 문제 아님

이었다.


무엇이 실패한 것인가?

실제로 실패한 것은

Private Key 로딩

이었다.

Integrity는 SSL 초기화 과정에서 다음 작업을 수행한다.

certificate.p12 열기
↓
비밀번호 확인
↓
Private Key 읽기
↓
SSLContext 생성
↓
HTTPS 서비스 시작

이번 장애는

certificate.p12 열기
성공

비밀번호 확인
성공

Private Key 읽기
실패

상황이었다.


왜 Private Key를 읽지 못했을까?

여기서 중요한 사실이 하나 있다.

이번 인증서 갱신 과정에서

cacerts
jssecacerts

는 모두 정상적으로 갱신되었다.

즉,

Root CA 정상
Intermediate CA 정상
TrustStore 정상

이었다.

문제는 오직

certificate.p12

에 있었다.


OpenSSL 3.x와 Java 8의 충돌

최근 OpenSSL 3.x는 기본적으로 다음 알고리즘을 사용한다.

PBES2
PBKDF2
AES-256
SHA256

최신 보안 표준 관점에서는 매우 좋은 선택이다.

하지만 Java 8 기반 제품은 이러한 최신 PKCS12 포맷을 완벽하게 지원하지 못하는 경우가 있다.


Java 8 시절의 PKCS12

Java 8 기반 시스템에서 일반적으로 사용되던 방식은

PBE-SHA1-3DES
SHA1 MAC

이었다.

Java 8
=
레거시 PKCS12 포맷

을 기대한다.


기존에는 왜 문제가 없었을까?

과거 인증서 교체 이력을 확인해 보니

다음 방식으로 작업하고 있었다.

keytool -importkeystore

JKS
↓
Java Keytool
↓
PKCS12

변환 과정을 거쳤다.

Java가 직접 생성한 PKCS12 파일이므로 Java 8 환경과의 호환성 문제가 발생하지 않았다.


이번에는 무엇이 달랐을까?

이번 작업에서는

서버 인증서(.crt)
개인키(.key)
체인 인증서(.crt)

를 이용하여 OpenSSL로 직접 PKCS12를 생성하였다.

예를 들면

openssl pkcs12 -export

명령을 사용하였다.

이 경우 OpenSSL 3.x는 최신 PKCS12 암호화 포맷을 사용하게 된다.

결과적으로

OpenSSL 3.x
↓
최신 PKCS12 생성
↓
Java 8
↓
Private Key 해석 실패

상황이 발생하였다.


해결 방법

Java 8과 호환되는 방식으로 PKCS12를 재생성하였다.

openssl pkcs12 -export `
-in "server.crt" `                 # 서버 인증서 (*.domain.com)
-inkey "server.key" `              # 서버 개인키 (Private Key)
-certfile "chain.crt" `            # 중간 인증서 및 루트 인증서 체인
-out "certificate.p12" `           # 생성될 PKCS12 파일
-name "tomcat" `                   # KeyStore 내부 Alias 이름
-keypbe PBE-SHA1-3DES `            # 개인키 암호화 알고리즘 (Java 8 호환)
-certpbe PBE-SHA1-3DES `           # 인증서 암호화 알고리즘 (Java 8 호환)
-macalg sha1                       # PKCS12 무결성 검증용 MAC 알고리즘

또는 기존 방식처럼

keytool -importkeystore

를 사용하는 방법도 있다.

실제로 레거시 제품에서는 이 방법이 가장 안전하다.


이번 장애에서 얻은건

인증서 갱신 시 많은 사람들이 아래만 확인한다.

인증서 유효기간
Root CA
Intermediate CA

 

하지만 실제 SSL 구성은

TrustStore
KeyStore
Private Key

모두 정상이어야 동작한다.

특히 Java 8 기반의 레거시 시스템에서는

certificate.p12 포맷
개인키 암호화 방식
Java 버전

까지 고려해야 한다.


결론

이번 장애의 원인은 인증서 자체가 아니었다.

cacerts 정상
jssecacerts 정상
인증서 체인 정상
인증서 유효기간 정상

이었음에도 불구하고

certificate.p12 내부 Private Key 포맷

이 Java 8 기반 Integrity와 호환되지 않아 SSL 초기화에 실패하였다.

결국 문제의 본질은

인증서 문제
❌

TrustStore 문제
❌

PKCS12 내부 Private Key 포맷 호환성 문제
⭕

였다.

레거시 Java 시스템을 운영하고 있다면 인증서 갱신 시 단순히 인증서만 교체할 것이 아니라 PKCS12 생성 방식과 Java 버전 호환성까지 반드시 확인해야 한다.

'Java' 카테고리의 다른 글

Comparator와 Comparable  (0) 2024.05.20
자바의 정렬의 모든 것  (2) 2024.05.19
Arrays.copyOf가 왜 빠를까  (0) 2024.05.10

Related Posts

질문이나 보충 의견이 있다면 댓글로 남겨주세요.

오류 제보, 추가 사례, 실무 경험 공유 모두 환영합니다.

📄 본문 💬 댓글