본문 바로가기

Back-End

금융기관 API에서 사용하는 Signed JWT + RFC8785 메시지 서명 구조 정리

반응형

일반적인 JWT는 로그인 토큰(Access Token) 용도로 많이 쓰인다.
하지만 금융이나 기관 연동에서는 JWT를 “데이터 자체의 서명” 용도로 사용한다.

이 글에서는 다음 구조를 정리한다.

  • RFC8785 JSON Canonicalization
  • Digest(SHA-256)
  • Detached Signed JWT(JWS)
  • x5c 인증서 체인 검증

이 조합은 단순 인증이 아니라,

“이 응답 데이터가 누가 만들었고, 중간에 변조되지 않았는가”

를 증명하기 위한 메시지 레벨 보안 방식이다.

 

1. Digest란 무엇인가?

Digest는 해시(Hash) 결과다.

암호화(encryption)가 아니라

  • 원본 → SHA-256 같은 해시 함수 → 고정 길이 값
  • 복원 불가능
  • 목적은 무결성 검증

data → hash → digest

 

JWT 서명은 실제로 “원문”을 직접 서명하는 게 아니라

canonicalized bytes → digest → private key sign

이 흐름이다.

 

2. 왜 RFC8785(JSON Canonicalization)가 필요한가?

JSON은 표현이 자유롭다.

아래는 의미는 같지만 바이트는 다르다.

{"a":1,"b":2}
{
  "b": 2,
  "a": 1
}

하지만 해시는 바이트 기준으로 계산된다.

그래서 송신자와 수신자가 같은 JSON을 가지고 있어도:

  • 공백
  • 키 순서
  • 숫자 표현

차이로 digest가 달라질 수 있다.

이를 해결하기 위해 RFC8785가 사용된다.

RFC8785는:

  • key 정렬
  • 공백 제거
  • 숫자 표현 통일
  • UTF-8 강제

등을 정의해 항상 동일한 바이트 시퀀스를 만들도록 한다.

즉:

JSON → RFC8785 → deterministic bytes → digest

 

 

 

3. Digest 알고리즘은 어떻게 결정되는가?

Digest에 사용하는 알고리즘은 보통 SHA-256이며,
JWT Header의 alg 필드에 포함된다.

 

예:

{
  "alg": "RS256"
}

 

의미:

  • RSA 서명
  • SHA-256 digest

중요한 점:

수신자는 반드시 허용 알고리즘을 미리 고정해야 한다.

단순히 header의 alg를 그대로 믿으면 downgrade 공격이 가능하다.


4. Detached JWS 구조 (로그인 JWT와 다름)

이 패턴은 로그인 JWT와 완전히 다르다.

보통 JWT는:

header.payload.signature

형태지만,

메시지 서명에서는 Detached JWS 를 사용한다:

header..signature

(payload가 비어 있음)

실제 서명 대상은 JWT 안이 아니라

HTTP Response Body임

 

JWT는 단지

  • 서명(signature)
  • 인증서 정보(x5c)

를 담는 컨테이너 역할만 한다.

즉 구조는,

HTTP Body (실제 데이터)
+
Detached Signed JWT (서명 껍데기)

 

 

더보기

지피티 보충 설명

1️⃣ HTTP Body

 
 
{
"orderId": "12345",
"amount": 10000,
"status": "SUCCESS"
}
 

이건 그냥 순수 JSON 데이터야.
서명값 없음.


2️⃣ HTTP Header (예시)

 
 
X-JWS-Signature: eyJhbGciOiJSUzI1NiIsIng1YyI6W119..QmFzZTY0U2lnbmF0dXJl
 

여기에 들어있는 게:

 
 
Base64(header)..Base64(signature)
 

즉, Detached JWS


그럼 실제 서명 대상은 뭐냐?

이 순수 Body를:

 
 
Body JSON

RFC8785 Canonicalization

SHA-256 Digest

PrivateKey Sign
 

이렇게 해서 나온 signature가
JWT 안에 들어있는 거야.


왜 payload가 비어 있냐?

일반 JWT는

 
 
header.payload.signature
 

이지만,

Detached JWS는

 
 
header..signature
 

payload 부분이 비어 있음.

왜냐면:

실제 payload는 이미 HTTP Body로 따로 있으니까
JWT 안에 또 넣지 않는 것.


그래서 최종 구조는 이렇게 이해하면 정확해

 
 
HTTP Response
├─ Body → 실제 데이터(JSON)
└─ Header → Detached JWS (서명 + x5c)
 

즉:

Body 안에 서명이 들어오는 게 아니라
Body를 대상으로 한 서명이 따로 전달되는 것.


비유하면

Body = 계약서 내용
Detached JWS = 계약서에 찍힌 도장

도장이 계약서 안에 또 인쇄된 게 아니라,
“이 계약서에 찍은 도장”을 따로 붙여주는 구조야.

 

 


5. x5c 인증서와 chaining 검증

JWT Header에는 x5c 필드가 들어갈 수 있다.

이는

  • Leaf 인증서
  • Intermediate CA
  • (선택) Root CA

로 구성된 인증서 체인이다.

검증 흐름은 다음과 같다:

  1. JWT에서 x5c 추출
  2. 인증서 체인 연결 검증
  3. 최상단 Root가 TrustStore에 있는지 확인
  4. Leaf 인증서에서 공개키 추출
  5. 공개키로 JWT 서명 검증

중요한 포인트:

토큰 안의 x5c만 믿으면 안 된다.

반드시 외부 TrustStore / CA / Pinning 으로 신뢰 검증이 들어가야 한다.

그렇지 않으면 공격자가 자기 인증서를 넣어도 통과한다.

 

6. 전체 검증 흐름 요약

송신 측

  1. Response Body 생성
  2. RFC8785 Canonicalization
  3. SHA-256 Digest
  4. Private Key 서명
  5. Detached JWS 생성 (x5c 포함)

수신 측

  1. Response Body RFC8785
  2. Digest 재계산
  3. JWT에서 x5c 추출
  4. 인증서 체인 검증
  5. 공개키로 서명 검증
  6. JWT Claims 검증(필요 시)

 

7. 왜 HTTPS 말고 이런 구조를 쓰는가?

HTTPS는 전송 구간만 보호한다.

하지만 금융/기관 시스템은 다음이 필요하다:

  • 누가 생성했는지 암호학적으로 증명
  • 중간 시스템(MQ, 로그, 저장소)을 지나도 변조 감지
  • 나중에 감사(Audit) 가능

그래서 “전송 보안”이 아니라:

👉 메시지 자체에 서명

하는 구조를 사용한다.


정리

✅ 응답 데이터 무결성 보장
✅ 발신 기관 증명
✅ 감사 대응 가능

을 목표로 하는 메시지 서명 아키텍처다.

핵심 구성 요소:

  • RFC8785 (JSON Canonicalization)
  • SHA-256 Digest
  • Detached Signed JWT
  • x5c Certificate Chain Verification
반응형

'Back-End' 카테고리의 다른 글

SSHJ란 무엇인가  (0) 2026.03.19
AES-256-GCM 암호화 방식 정리  (0) 2026.03.17
jstat로 JVM 메모리 모니터링: 실무용 해석 가이드  (3) 2025.08.12
웹소켓 정리 2  (0) 2025.05.07
웹소켓 정리  (1) 2025.05.06