서명된 JWT를 만드는 방법
- 알고리즘을 고르세요. 헤더 (JSON) 의
alg필드가 선택을 자동으로 따라갑니다. - 페이로드 (JSON) 를 편집하세요. + iat 현재는 현재 시각을 넣고, + exp 1시간 후는 지금부터 한 시간 뒤를 만료 시각으로 설정합니다. 둘 다 JWT 명세대로 초 단위를 씁니다.
- HS256, HS384, HS512라면 시크릿을 입력하고 시크릿 형식을 텍스트, Base64, Hex 중 하나로 설정하세요. 다른 알고리즘이라면 개인 키를 붙여넣거나 키 쌍 생성을 누르세요.
- 토큰 복사를 누르거나, 디코더에서 열기로 확인하세요. 토큰과 검증 키가 채워진 상태로 디코딩 및 검증 탭이 열립니다.
처음 열 때 들어 있는 예시는 시크릿 base64hub-demo-secret으로 서명된 실제 HS256 토큰이므로, 클레임 하나를 바꾸고 서명이 함께 바뀌는 것을 볼 수 있습니다.
알고리즘 고르기
| 알고리즘 | 키 유형 | 검증할 수 있는 사람 | 토큰 안의 서명 |
|---|---|---|---|
| HS256 | 공유 시크릿 (HMAC-SHA256) | 같은 시크릿을 가진 누구나 | 43문자 |
| RS256, PS256 | 2048비트 이상의 RSA 개인 키 | 공개 키를 가진 누구나 | 342문자 (RSA-2048) |
| ES256 | P-256 곡선의 EC 개인 키 | 공개 키를 가진 누구나 | 86문자 |
| EdDSA | Ed25519 개인 키 | 공개 키를 가진 누구나 | 86문자 |
같은 백엔드가 확인하는 세션 토큰처럼 한 서비스가 서명과 검증을 모두 한다면 HS256이 가장 간단합니다. 두 번째 당사자가 검증해야 하는 순간, HMAC 시크릿을 공유하면 그쪽도 토큰을 발급할 수 있게 되므로 비대칭 알고리즘으로 바꾸고 공개 키만 공개하세요. ES256과 EdDSA는 RSA보다 토큰이 훨씬 짧아 쿠키와 헤더에서 유리합니다. PS256은 PSS 패딩 방식을 쓰는 RSA이며, 모든 검증 쪽이 지원할 때만 고르세요.
none은 파서 테스트용으로 목록에 있습니다. alg: none 토큰은 서명이 비어 있어서 이를 받아들이는 서버는 누구에게나 속을 수 있으므로, 도구가 경고합니다.
이 인코더가 받고 생성하는 키
서명에는 PEM PKCS#8 키(BEGIN PRIVATE KEY), PKCS#1 RSA PRIVATE KEY, SEC1 EC PRIVATE KEY, 개인 JWK를 받습니다. 생성기 대신 OpenSSL로 테스트 키를 만들려면 다음과 같이 합니다.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out rsa.pem
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ec.pem
openssl genpkey -algorithm ed25519 -out ed.pem
openssl pkey -in ec.pem -pubout -out ec.pub
키 쌍 생성은 알고리즘에 따라 RSA-2048, P-256, P-384, P-521, Ed25519 키를 브라우저에서 WebCrypto로 만듭니다. 개인 키는 PKCS#8 PEM으로, 공개 키는 SPKI PEM으로, 그리고 kid가 키의 RFC 7638 지문인 JWKS를 받습니다. JWKS는 검증하는 쪽이 내려받도록 jwks_uri에서 제공할 내용입니다.
HS256에서 시크릿은 바이트로 쓰입니다. 서버가 환경 변수에서 시크릿을 읽어 먼저 Base64 디코딩한다면 여기서도 시크릿 형식을 Base64로 설정하세요. 그렇지 않으면 서명이 일치하지 않습니다. Base64 키 생성기의 JWT 시크릿 프리셋 값은 Base64URL이며 32바이트로 디코딩됩니다.
페이로드에 넣을 클레임
쓸모 있는 액세스 토큰에는 보통 iss(발급자), sub(토큰이 가리키는 대상), aud(대상 API), iat, 그리고 짧은 exp가 들어갑니다. 개별 토큰을 폐기해야 한다면 jti를 추가하세요. 시각은 1970년 이후의 정수 초입니다. JavaScript로 만든다면 Date.now()는 밀리초를 돌려주므로 Math.floor(Date.now() / 1000)을 쓰세요.
페이로드는 서명될 뿐 암호화되지 않습니다. 토큰을 본 사람은 누구나 JWT 디코더로 디코딩할 수 있으므로, 로그에 찍히면 안 될 비밀번호, API 키, 개인 정보는 넣지 마세요.
코드에서 JWT 서명하기
형식이 단순해서 직접 만들 수 있으며, 라이브러리를 디버깅할 때 도움이 됩니다. 이 Node.js 함수는 같은 입력에 대해 PyJWT의 jwt.encode(payload, secret, algorithm="HS256")와 정확히 같은 토큰을 만듭니다.
const crypto = require("crypto");
const b64url = (obj) => Buffer.from(JSON.stringify(obj)).toString("base64url");
function signHS256(payload, secret) {
const input = b64url({ alg: "HS256", typ: "JWT" }) + "." + b64url(payload);
const sig = crypto.createHmac("sha256", secret).update(input).digest("base64url");
return input + "." + sig;
}
console.log(signHS256({ sub: "user-42", iat: 1767225600, exp: 1767229200 },
"replace-me-with-32-random-bytes!"));
출력은 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTQyIiwiaWF0IjoxNzY3MjI1NjAwLCJleHAiOjE3NjcyMjkyMDB9.F3xnXnXW8nUQy-Ay37DF626Lb5lHPKI3IFvqoEf3mTw입니다. 이 페이지에 같은 헤더, 페이로드, 시크릿을 넣으면 같은 문자열이 나옵니다. 도구는 서명 전에 JSON을 압축하므로 들여쓰기는 상관없지만, 서명이 정확한 바이트를 대상으로 하기 때문에 키 순서는 중요합니다. 운영 환경에서는 관리되는 라이브러리를 쓰세요. 인코딩 쪽은 Node.js의 Base64를 참고하세요.
자주 묻는 질문
HS256 시크릿은 얼마나 길어야 하나요?
최소 256비트, 즉 무작위 32바이트입니다. RFC 7518은 키가 해시 출력 길이 이상이어야 한다고 요구합니다. 비밀번호 같은 짧은 시크릿은 토큰 하나만 있어도 오프라인에서 무차별 대입으로 알아낼 수 있으므로 Base64 키 생성기로 만드세요.
HS256과 RS256 중 무엇을 써야 하나요?
같은 서비스가 토큰을 발급하고 검증한다면 HS256입니다. 양쪽이 시크릿 하나를 공유하기 때문입니다. 다른 당사자가 검증해야 한다면 RS256, ES256, EdDSA를 쓰세요. 그들은 공개 키만 받으므로 직접 토큰을 발급할 수 없습니다.
개인 키에 오류가 표시되는 이유는 무엇인가요?
비밀번호로 보호된 키(BEGIN ENCRYPTED PRIVATE KEY)는 거부됩니다. openssl pkey -in key.pem -out plain.pem으로 암호를 제거하고 결과를 붙여넣으세요. 키 유형도 알고리즘에 맞아야 합니다. EC 키로는 RS256 서명을 할 수 없습니다.
토큰에 kid를 추가하려면 어떻게 하나요?
키 쌍 생성 후 JWKS 출력에서 kid를 복사해 헤더 JSON에 추가하세요. 예: "kid": "…". 검증하는 쪽은 이것으로 키 집합에서 맞는 키를 고릅니다.