JSON Canonicalization: Define Stable Signature Input

Choose raw-byte signing or a defined JSON canonicalization scheme, reject ambiguous data and test interoperability before generating hashes or signatures.

In this article

Two JSON documents can represent the same object while containing different bytes. Whitespace, property order and number formatting can change a raw hash. If a signature authenticates JSON data rather than the exact received bytes, producer and verifier need a shared canonicalization contract.

Do not invent that contract by removing spaces or sorting top-level keys. Choose either exact-byte signing or a documented canonical representation, then test both ends. A valid JSON parser and a cryptographic hash do not by themselves establish compatible signature input.

Decide what the signature protects

With exact-byte signing, the sender signs the original payload bytes and the receiver verifies those same bytes before any transformation. This is often appropriate for webhooks whose protocol already defines it. Reformatting the document before verification breaks the agreement.

With data-level signing, the protocol defines how a supported JSON value becomes canonical bytes. The verifier parses and canonicalizes according to the same rules. That design can tolerate cosmetic serialization differences, but it needs a precise supported data model.

Write the decision into the integration specification. Include character encoding, signature algorithm, key identification and failure behavior. Canonicalization solves one representation problem; it does not choose a safe key-management or authorization design for you.

Use a defined scheme where required

RFC 8785 defines the JSON Canonicalization Scheme. Its rules go beyond visual formatting. Use an implementation that explicitly supports the required scheme and its input constraints rather than assuming your language's default serializer is equivalent.

json
{"amount":12,"currency":"USD","reference":"fixture-1"}

Reordering these properties can preserve the parsed object's meaning while changing raw bytes. This example illustrates the distinction; it is not a complete canonicalization test vector.

Inspect structural differences with JSON Diff. Generate a digest of an ordinary test string with SHA-256 Generator to see that byte changes affect a hash. Neither tool claims to implement a signature protocol or canonicalization scheme.

Reject ambiguous inputs early

Duplicate property names are a major interoperability problem. Different parsers can keep different values or expose duplicates differently. Decide how the protocol rejects them before a normal object parser silently discards the evidence.

Also constrain numbers to the supported representation. Large identifiers are often better represented as strings if exact preservation matters across languages. A value rounded by a parser cannot be recovered by sorting keys afterward.

The JSON specification discusses interoperability considerations. Use those constraints to design inputs that both endpoints can faithfully represent. Do not accept a wider local data model and assume the remote verifier will interpret it identically.

Keep normalization decisions explicit

Canonicalization does not automatically mean changing Unicode text, lowercasing strings or rounding currency. Those operations change the data. If the business protocol requires them, define them as a separate preparation step and test their effect.

Preserve arrays in their specified order unless the protocol explicitly defines another representation. Sorting object properties is not a reason to sort every list. A list of workflow steps can change meaning when reordered.

Keep timestamps, decimals and identifiers in documented formats. A signature system should not decide at runtime that two visually similar values are equivalent without a shared rule. For precise time representations, see RFC 3339 Timestamp Precision.

Test cross-language agreement

Create fixtures with nested objects, arrays, escaped characters, Unicode and boundary numbers. Include invalid cases that must be rejected. For each accepted fixture, compare canonical bytes before comparing signatures.

When signatures differ, inspect the first byte difference using a controlled development tool. Do not publish keys or confidential payloads while debugging. Comparing only parsed objects can hide a serialization mismatch; comparing only signature values does not tell you where it occurred.

Run the fixtures in every language or runtime used by the integration. A test that passes twice in the same implementation proves less than a sender/verifier interoperability test. Pin the canonicalization library and record its version with the protocol implementation.

Protect the verification boundary

Verify before acting on authenticated data. Do not perform a state-changing operation and then report a signature error afterward. Ensure the application consumes the same validated value that the verifier authenticated.

Include replay protection where the protocol needs it, using its specified timestamp, nonce or event identifier. A valid signature can still accompany an old message. Canonicalization does not stop repeated delivery or establish that an action remains authorized.

Separate malformed input, unsupported data and invalid signatures in internal diagnostics. Keep external errors appropriately limited so they help legitimate integration developers without exposing sensitive verification details.

Keep fixtures for rejection behavior as well as successful signatures. A verifier that rejects ambiguous input is behaving differently from one that silently repairs it. The sender needs to know which result to expect, and the application must not turn rejected data into a partially accepted action.

Stable input is a protocol decision

Choose raw bytes or a defined canonical form, reject ambiguous values and compare byte-level fixtures across implementations. Only then add hashing and signing. The result should be a representation both endpoints agree on, not merely JSON that looks neatly formatted.

Advertisement