My solution to this was to just serialize it (to say json) and then just hmac the serialized data.
The hmac will only work on the json that was provided.
Trying to get 2 systems to serialize exactly the same is not realistic.
Doing the HMAC on the serialized data guarantees that there will be no way the HMAC could be calculated wrong, HMAC'ing an object in memory will always be contingent on things specific to the operating environment (such as NodeJS vs .NET vs Go) and internal representations (.NET decimal
/float
/double
vs NodeJS number
).
Futhermore, even if you happen to use the same language and framework, the receiver may not use the same serializer (Newtonsoft
vs DataContracts
vs JsonSerializer
), and to get it 100% consistent is not sustainable.
Instead the solution is to perform the HMAC on the resulting JSON string, as performing an HMAC on a UTF-8 string is consistent between all operating environments as the deserialization is irrelevant.