일반적인 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 (예시)
여기에 들어있는 게:
즉, Detached JWS
그럼 실제 서명 대상은 뭐냐?
이 순수 Body를:
↓
RFC8785 Canonicalization
↓
SHA-256 Digest
↓
PrivateKey Sign
이렇게 해서 나온 signature가
JWT 안에 들어있는 거야.
왜 payload가 비어 있냐?
일반 JWT는
이지만,
Detached JWS는
payload 부분이 비어 있음.
왜냐면:
실제 payload는 이미 HTTP Body로 따로 있으니까
JWT 안에 또 넣지 않는 것.
그래서 최종 구조는 이렇게 이해하면 정확해
├─ Body → 실제 데이터(JSON)
└─ Header → Detached JWS (서명 + x5c)
즉:
Body 안에 서명이 들어오는 게 아니라
Body를 대상으로 한 서명이 따로 전달되는 것.
비유하면
Body = 계약서 내용
Detached JWS = 계약서에 찍힌 도장
도장이 계약서 안에 또 인쇄된 게 아니라,
“이 계약서에 찍은 도장”을 따로 붙여주는 구조야.
5. x5c 인증서와 chaining 검증
JWT Header에는 x5c 필드가 들어갈 수 있다.
이는
- Leaf 인증서
- Intermediate CA
- (선택) Root CA
로 구성된 인증서 체인이다.
검증 흐름은 다음과 같다:
- JWT에서 x5c 추출
- 인증서 체인 연결 검증
- 최상단 Root가 TrustStore에 있는지 확인
- Leaf 인증서에서 공개키 추출
- 공개키로 JWT 서명 검증
중요한 포인트:
토큰 안의 x5c만 믿으면 안 된다.
반드시 외부 TrustStore / CA / Pinning 으로 신뢰 검증이 들어가야 한다.
그렇지 않으면 공격자가 자기 인증서를 넣어도 통과한다.
6. 전체 검증 흐름 요약
송신 측
- Response Body 생성
- RFC8785 Canonicalization
- SHA-256 Digest
- Private Key 서명
- Detached JWS 생성 (x5c 포함)
수신 측
- Response Body RFC8785
- Digest 재계산
- JWT에서 x5c 추출
- 인증서 체인 검증
- 공개키로 서명 검증
- 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 |