Skip to content
AZ Tools

JSON Canonicalize (RFC 8785 JCS)

Canonicalize JSON per RFC 8785 and hash it: UTF-16 key order, ECMAScript number form, no whitespace, with a SHA-256 of the exact bytes.

How to use

  1. Paste the JSON. The canonical form, its UTF-8 byte count and the SHA-256 of those exact bytes appear below.
  2. Load the specification's own sorting test to see the UTF-16 rule put an emoji before a Hebrew letter.
  3. Check the input problems panel before signing anything: a duplicate key or an over-long number changes the bytes without changing what you see.
  4. Hash the bytes, not the text in the box — the byte count tells you what was actually digested.

About this tool

Show

Turn a JSON document into the one byte sequence RFC 8785 says it must have, and hash those bytes, because producing a stable hash is the only reason to canonicalize at all. The scheme exists so that a signature over JSON survives being reformatted, reordered or round-tripped through a parser: whitespace goes, property names are sorted, numbers are rewritten in ECMAScript's own form, and the result is encoded as UTF-8. Three details cause most interoperability failures and this tool shows each of them. Property names are sorted on their UTF-16 code units, not their code points, so a character outside the Basic Multilingual Plane — an emoji, say — sorts before characters in the U+E000 to U+FFFF range rather than after them; the key order panel lists the sorted names with their code points so the difference is visible. Numbers are the double they parse to and not the literal you wrote, so 1.0, 1e0 and 1 all canonicalize to 1, and a literal beyond double precision quietly becomes something else. And a duplicate property name is silently collapsed by every JSON parser, keeping the last — the specification requires I-JSON input, which forbids duplicates, so the tool scans the source text for them rather than trusting the parsed object. A lone surrogate is refused outright rather than escaped, because the specification requires a compliant implementation to stop: emitting it would produce bytes that a verifier reading the same document might not reproduce. For a readable sorted document rather than bytes to sign, the plain key sorter is the gentler tool.

Frequently asked questions

Why sort on UTF-16 code units and not code points?

Because the specification says so, and the reason is practical: UTF-16 is how strings are represented in ECMAScript, Java and .NET, and JSON escape sequences are themselves UTF-16 code units. The two orders differ only for characters outside the Basic Multilingual Plane, which sort as a surrogate pair beginning U+D800–U+DBFF — so an emoji lands before U+E000 rather than after U+FFFF. An implementation that sorts by code point is deterministic but not JCS, and its signatures will not verify against one that is.

How is this different from just sorting the keys?

Sorting keys gives you a readable diff; canonicalizing gives you bytes to sign. JCS also removes all whitespace, rewrites every number in ECMAScript form, fixes the escaping of every string, encodes as UTF-8, and refuses invalid input. If you only want a tidy document to commit, the key sorter is simpler and does not rewrite your numbers.

Why did my number change?

Because JSON numbers are doubles here, and the canonical form is the double, not the text. 1.0 and 1e0 both become 1; 1E30 becomes 1e+30; 0.002 stays 0.002 but 2e-3 also becomes 0.002. A literal with more precision than a double holds is rounded on the way in, so 9007199254740993 comes out as 9007199254740992. The tool flags any literal whose spelling changes.

Why refuse a lone surrogate instead of escaping it?

The specification requires it. A lone surrogate is not valid Unicode, so different implementations handle it differently — some escape it, some replace it, some throw — and any disagreement means the same document produces different bytes and a signature that verifies in one place and fails in another. Stopping is the only interoperable answer.

Does a duplicate key really matter?

For signing, yes. JSON.parse keeps the last of two identical names and discards the first without a word, so a document can carry a value a verifier never sees while hashing to something that looks correct. RFC 8785 sidesteps it by requiring I-JSON input, which forbids duplicates outright — so they have to be caught in the text, before parsing.

Related tools