본문으로 건너뛰기
AZ Tools

HKDF 키 유도 (RFC 5869, 추출·확장)

HKDF로 키를 유도하고 두 단계를 모두 봅니다. 추출 단계의 PRK, 확장 단계의 T 블록, hex와 base64 OKM. RFC 5869 케이스 내장.

사용법

  1. 입력 키 재료를 넣으세요. 각 칸은 기본적으로 hex로 읽고, 체크박스로 텍스트로 바꿀 수 있습니다. 공유 비밀은 바이트로 붙여 넣고 라벨은 글자로 입력하면 됩니다.
  2. 솔트가 있으면 넣고, info에는 그 키가 무엇을 위한 것인지 적으세요. 프로토콜, 방향, 버전 같은 것입니다. 둘 다 비워 둘 수 있고, 솔트를 비우면 무엇으로 대체했는지 페이지가 알려 줍니다.
  3. 해시와 출력 길이(바이트)를 고르세요. 표시된 한계는 해시 출력의 255배이고, 명세가 거기서 멈춥니다.
  4. PRK, T 블록, OKM을 읽으세요. 아래 줄이 브라우저 자체 HKDF가 같은 바이트를 반환했는지 말해 줍니다. 만약 아니라고 하면 그 출력은 쓰지 마세요.

자세한 설명

펼치기

HKDF는 강한 비밀 하나를 필요한 만큼의 독립적인 키로 바꿔 주는 키 유도 함수이고, 한 단계가 아니라 두 단계입니다. 추출(extract)은 입력 키 재료를 고정 길이 의사난수 키로 압축합니다. PRK = HMAC-Hash(salt, IKM)이고, 추출 단계는 그것이 전부입니다. 확장(expand)은 그 PRK를 HMAC 연쇄로 원하는 길이까지 늘립니다. T(i) = HMAC(PRK, T(i-1) ‖ info ‖ i)를 이어 붙여 길이만큼 자릅니다. 이 페이지는 둘 다 보여 주는데, 브라우저는 보여 주지 않기 때문에 그게 중요합니다.

crypto.subtle.deriveBits는 추출과 확장을 한 호출로 합쳐 놓고 PRK를 절대 돌려주지 않습니다. HKDF 키 객체는 내보낼 수도 없어서 바이트를 요구하면 InvalidAccessError가 납니다. 즉 API를 통해서는 중간값을 볼 방법이 아예 없습니다. 그래서 이 도구는 두 단계를 HMAC으로 직접 계산해 PRK와 각 T 블록을 출력하고, 그다음 브라우저에 같은 답을 물어 둘이 일치하는지 알려 줍니다. 두 구현이 합의하는 것이 하나가 주장하는 것보다 값집니다.

info 필드는 대부분 그냥 건너뛰는 부분이자, HKDF가 지금 같은 모양인 이유입니다. 같은 IKM과 솔트에 info만 다르면 서로 계산 가능한 관계가 없는 키가 나옵니다. TLS 1.3이 하나의 핸드셰이크 비밀에서 방향별·용도별 키를 따로 얻는 방식이 바로 이것이고, 긴 출력 하나를 조각내 나눠 쓰며 잘 되기를 바라는 것과는 다릅니다. 한 비밀에서 여러 키를 뽑는다면 그 키들은 오프셋이 아니라 info로 달라야 합니다.

명세가 두는 제한 두 가지는 브라우저도 강제합니다. 출력은 255 × HashLen으로 제한됩니다. SHA-256이면 8160바이트입니다. 확장 안에서 붙이는 블록 카운터가 1바이트이기 때문입니다. 더 요구하면 이 페이지와 deriveBits 둘 다 거부합니다. 그리고 솔트가 없으면 0 바이트 HashLen개로 바뀌는데, 이는 관례처럼 들리다가 직접 해 보면 달라집니다. Web Crypto는 길이 0인 HMAC 키를 DataError: HMAC key data must not be empty로 거부하므로, 그 대체 없이는 브라우저에서 추출 단계를 계산할 수조차 없습니다.

프리셋 네 개는 RFC 5869의 테스트 케이스 1, 3, 4, 7이라서 한 번 눌러 명세와 대조할 수 있습니다. 케이스 1의 PRK는 077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5이고 OKM은 3cb25f25faacd57a로 시작합니다. 케이스 3은 같은 IKM에 솔트도 info도 없는 경우인데, 결과가 완전히 다른 곳에 떨어집니다 — 19ef24a32c717b16… — 추출 단계가 형식적인 절차가 아니기 때문입니다.

HKDF가 아닌 것 하나. 비밀번호용 KDF가 아닙니다. 출력 블록마다 HMAC을 한 번 돌릴 뿐 반복 횟수도 메모리 비용도 없어서, 비밀번호를 찍어 보는 공격자에게 아무 부담도 주지 않습니다. 입력이 이미 엔트로피가 높다고 가정합니다. 비밀번호라면 PBKDF2, scrypt, Argon2를 쓰세요.

자주 묻는 질문

Web Crypto는 안 주는 PRK를 이 도구는 왜 보여 주나요?

다른 구현과 맞춰 보는데 출력이 다를 때 필요한 값이기 때문입니다. PRK를 보면 어긋난 곳이 추출인지 확장인지 알 수 있습니다. crypto.subtle.deriveBits는 두 단계를 한 호출로 수행하고 최종 바이트만 돌려주며, 가져온 HKDF 키는 내보낼 수 없어 exportKey가 InvalidAccessError를 냅니다. 추출이 문자 그대로 HMAC-Hash(salt, IKM)이므로, 이 페이지는 crypto.subtle.sign으로 다시 계산해 보여 줍니다.

info는 무엇에 쓰고, 비워 둬도 되나요?

비워 둬도 되고 RFC 5869도 허용합니다. 다만 info는 HKDF를 그냥 해시보다 쓸 만하게 만들어 주는 부분입니다. 유도한 키를 맥락에 묶어 주므로, 같은 비밀이 서로 무관한 용도에 서로 무관한 키를 내놓습니다. 애플리케이션 이름, 방향, 프로토콜 버전처럼 모호하지 않고 버전이 붙은 값을 쓰고, 조각을 이어 붙일 때 혼동이 생기지 않도록 하세요. "ab" ‖ "c"와 "a" ‖ "bc"는 같은 바이트이기 때문입니다.

출력이 255 블록으로 제한되는 이유는?

확장이 HMAC 입력마다 1바이트 카운터를 붙이므로 255까지밖에 셀 수 없습니다. SHA-1이면 5100바이트, SHA-256이면 8160바이트, SHA-512면 16320바이트입니다. 그보다 더 필요하다면, 거대한 출력 하나가 아니라 info가 다른 여러 번의 유도를 원하는 경우가 거의 확실하고, 그쪽이 관행상으로도 낫습니다.

비밀번호에 HKDF를 써도 되나요?

쓸 수는 있지만 쓰면 안 됩니다. HKDF는 출력 블록마다 HMAC을 한 번 계산합니다. 입력이 이미 추측 불가능하다고 가정하므로 빠르게 설계된 것입니다. 비밀번호는 추측 가능하고, 비밀번호용 KDF의 일은 반복이나 메모리로 추측 한 번을 비싸게 만드는 것입니다. 반복 횟수를 높인 PBKDF2, scrypt, Argon2가 맞는 도구입니다. HKDF는 그다음, 그것들이 만들어 낸 키에서 여러 키를 뽑아야 할 때 쓰는 것입니다.

솔트는 비밀인가요?

아닙니다. 공개해도 되고, 평문으로 전송해도 되고, 생략해도 됩니다. 솔트의 역할은 더 넓은 범위의 입력에 대해 추출이 무작위성 추출기처럼 동작하게 만드는 것이고, 무언가를 숨기는 것이 아닙니다. 그래도 관리할 수 있다면 솔트를 두는 편이 좋습니다. 분석이 더 튼튼해지고 비용은 들지 않습니다. 비밀로 남아야 하는 것은 IKM과 거기서 나온 모든 것입니다.

관련 도구