본문으로 건너뛰기
AZ Tools

JSON 정규화 (RFC 8785 JCS)

RFC 8785대로 JSON을 정규화하고 해시합니다: UTF-16 키 순서, ECMAScript 숫자 표기, 공백 제거, 그리고 그 바이트의 SHA-256.

사용법

  1. JSON을 붙여 넣으면 정규 형식과 UTF-8 바이트 수, 그리고 그 바이트의 SHA-256이 아래에 나옵니다.
  2. 규격 자체의 정렬 테스트를 불러오면 UTF-16 규칙이 이모지를 히브리 문자보다 앞에 두는 것을 볼 수 있습니다.
  3. 무엇이든 서명하기 전에 입력 문제 패널을 확인하세요. 중복 키나 너무 긴 숫자는 눈에 보이는 것은 그대로 두고 바이트를 바꿉니다.
  4. 상자의 텍스트가 아니라 바이트를 해시하세요 — 바이트 수가 실제로 무엇이 다이제스트됐는지 알려 줍니다.

자세한 설명

펼치기

JSON 문서를 RFC 8785가 정한 단 하나의 바이트 나열로 바꾸고 그 바이트를 해시합니다. 애초에 정규화하는 이유는 안정된 해시를 얻는 것뿐이기 때문입니다. 이 방식은 JSON에 대한 서명이 재포맷·재정렬·파서 왕복을 견디게 하려고 존재합니다. 공백은 사라지고, 속성 이름은 정렬되고, 숫자는 ECMAScript 자체 표기로 다시 쓰이고, 결과는 UTF-8로 인코딩됩니다. 상호운용 실패는 대체로 세 가지에서 나오고, 이 툴은 그 각각을 보여 줍니다. 속성 이름은 코드 포인트가 아니라 UTF-16 코드 단위로 정렬되므로, BMP 밖 문자(예컨대 이모지)는 U+E000~U+FFFF 구간 문자들보다 뒤가 아니라 앞에 옵니다. 키 순서 패널이 정렬된 이름을 코드 포인트와 함께 나열해 그 차이를 보여 줍니다. 숫자는 당신이 쓴 리터럴이 아니라 파싱된 double이므로 1.0·1e0·1이 모두 1로 정규화되고, double 정밀도를 넘는 리터럴은 조용히 다른 값이 됩니다. 그리고 중복 속성 이름은 모든 JSON 파서가 마지막 것만 남기고 조용히 접어 버립니다 — 규격은 중복을 금지하는 I-JSON 입력을 요구하므로, 이 툴은 파싱된 객체를 믿지 않고 원본 텍스트를 훑습니다. 짝 없는 서로게이트는 이스케이프하지 않고 아예 거부합니다. 규격이 규격 준수 구현은 멈추라고 요구하기 때문입니다. 그대로 내보내면 같은 문서를 읽는 검증자가 재현하지 못할 바이트가 나옵니다. 서명할 바이트가 아니라 읽기 좋은 정렬 문서가 필요하면 단순 키 정렬 툴이 더 부드러운 선택입니다.

자주 묻는 질문

왜 코드 포인트가 아니라 UTF-16 코드 단위로 정렬하나요?

규격이 그렇게 정했고, 이유는 실용적입니다. UTF-16은 ECMAScript·자바·.NET에서 문자열이 표현되는 방식이고, JSON 이스케이프 시퀀스 자체가 UTF-16 코드 단위입니다. 두 순서는 BMP 밖 문자에서만 갈리는데, 그 문자는 U+D800~U+DBFF로 시작하는 서로게이트 쌍으로 정렬되므로 이모지가 U+FFFF 뒤가 아니라 U+E000 앞에 옵니다. 코드 포인트로 정렬하는 구현도 결정적이긴 하지만 JCS가 아니고, 그 서명은 JCS 구현에서 검증되지 않습니다.

그냥 키를 정렬하는 것과 무엇이 다른가요?

키 정렬은 읽기 좋은 diff를 주고, 정규화는 서명할 바이트를 줍니다. JCS는 공백을 모두 제거하고, 모든 숫자를 ECMAScript 표기로 다시 쓰고, 모든 문자열의 이스케이프를 고치고, UTF-8로 인코딩하고, 잘못된 입력을 거부합니다. 커밋할 깔끔한 문서만 원한다면 키 정렬기가 더 간단하고 숫자를 다시 쓰지도 않습니다.

숫자가 왜 바뀌었나요?

여기서 JSON 숫자는 double이고 정규 형식은 텍스트가 아니라 그 double이기 때문입니다. 1.0과 1e0은 모두 1이 되고, 1E30은 1e+30이 되며, 0.002는 그대로지만 2e-3도 0.002가 됩니다. double이 담는 정밀도를 넘는 리터럴은 들어올 때 반올림되므로 9007199254740993은 9007199254740992로 나옵니다. 이 툴은 표기가 바뀌는 리터럴을 모두 표시합니다.

짝 없는 서로게이트를 왜 이스케이프하지 않고 거부하나요?

규격이 요구합니다. 짝 없는 서로게이트는 유효한 유니코드가 아니라서 구현마다 다르게 다룹니다 — 이스케이프하기도, 치환하기도, 예외를 던지기도 합니다 — 그리고 그 불일치는 같은 문서가 다른 바이트를 내고 한쪽에서는 검증되고 다른 쪽에서는 실패하는 서명을 뜻합니다. 멈추는 것이 유일하게 상호운용되는 답입니다.

중복 키가 정말 문제가 되나요?

서명에서는 그렇습니다. JSON.parse는 같은 이름 둘 중 마지막 것을 남기고 앞의 것을 말없이 버리므로, 문서가 검증자는 보지 못하는 값을 담고 있으면서 겉보기에 올바른 해시를 낼 수 있습니다. RFC 8785는 중복을 아예 금지하는 I-JSON 입력을 요구해 이를 피해 가므로, 중복은 파싱 전에 텍스트에서 잡아야 합니다.

관련 도구