I need to sign some JSON data in Java and then send the data and sign to NodeJS.
Take a look at this example.
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.security.MessageDigest;
public class SomeClass {
private double n1 = 0.0;
private double n2 = 3.2544572232515347E-4;
private static String bytesToHex(byte[] bytes) {
StringBuffer result = new StringBuffer();
for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return result.toString();
}
private static String getSha256(String value) {
try{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(value.getBytes());
return bytesToHex(md.digest());
} catch(Exception ex){
throw new RuntimeException(ex);
}
}
public static void main(String[] args) {
SomeClass SC = new SomeClass();
Gson gson = new Gson();
String jsonString = gson.toJson(SC);
JsonParser parser = new JsonParser();
JsonElement jsonElement = parser.parse(jsonString);
JsonObject data = jsonElement.getAsJsonObject();
JsonObject rootObject = new JsonObject();
rootObject.addProperty("sign", getSha256(jsonString));
rootObject.add("data", data);
System.out.println(gson.toJson(rootObject));
}
}
After all I get this String {"sign":"c196a32af925e19897b00f430d9043854ccd5c109d2aee10ec15dfa2bbc060b9","data":{"n1":0.0,"n2":3.2544572232515347E-4}}
The problem happens when I try to parse JSON in JS and check sign.
function checkHash(data){
return data.key === sha(config.server_key + JSON.stringify(data.data) + config.server_key);
}
This function return false because the sign of data in JS is f5bcb176b734f6a38eae9f0a332a00ae3b798c918ec505bc50baac84b3b1a43a
And I if I look at JSON in JS it looks like {"n1":0,"n2":0.00032544572232515347}
So, there is a problem with double in parsing JSON in JS.
And there are some questions:
1) Maybe there is a way to parse JSON by levels(like GSON, dataObject.get("data").getAsString())? I searched a library but I coudn't find anything.
2) Maybe there is another way to sign JSON data in JAVA and check sign in JS?
I found that I can use custom JSONWriter in GSON, but I think there must be more simple way.
Update for people who may search this question
I used java.security.MessageDigest to make sha256, and it worked fine until I sent some long data. When I package program with Maven it starts making wrong hashes. Using org.apache.commons.codec.digest.DigestUtils.sha256Hex solved my problem.
1 Answer 1
The issue here is that data.data is a structure of variables in memory, not a simple stream of bytes you can hash. Depending on whitespace and ordering, there are many, many valid string representations of this data, one of which (presumably) hashes to c196a32af925e19897b00f430d9043854ccd5c109d2aee10ec15dfa2bbc060b9, and there is no way for JSON.stringify to know which you want. (I can't actually see any salting happening in your Java code though?)
if you want to check the signature, you'll need to either pass the object as
{ "data": "{\"value\": 1234}", "sig": "abcd" }
then parse it to get a string and a signature, verify they match, and then parse the string to get the data; or else move the signature to a header and check it against the entire JSON payload before you parse it. The latter is rather more elegant but may be harder to implement.
Equally of course you can do this out-of-the-box simply by using a TLS connection, but of course that might be tricky depending on your server &c.
console.log(3.2544572232515347E-4);It's resolves the value of3.2544572232515347 / 10^4and shows0.00032544572232515347. If you want to keep the value in its original state then pass it as a string, not a double.data.data.n2.toExponential(16)