说明
Web/互联网HTTP通信过程中,往往对数据的安全性、完整性构成挑战,以下是通过对JSON数据签名和验证签名的方式来保证传输数据的完整性。
签名的过程:
参考:数字签名
代码
package com.what21.apps.json.sign;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javax.crypto.spec.SecretKeySpec;
public class JSONSignTools {
/**
* @param data
* @param key
* @return
* @throws Exception
*/
public static String hmacSHA256(String data, String key) throws Exception {
String algorithm = "HmacSHA256";
javax.crypto.Mac mac = javax.crypto.Mac.getInstance(algorithm);
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), algorithm);
mac.init(secret_key);
byte[] array = mac.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* @param data
* @param key
* @return
* @throws Exception
*/
public static String hmacMD5(String data, String key) throws Exception {
String algorithm = "HmacMD5";
javax.crypto.Mac mac = javax.crypto.Mac.getInstance(algorithm);
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), algorithm);
mac.init(secret_key);
byte[] array = mac.doFinal(data.getBytes("UTF-8"));
return byteArrayToHexString(array).toUpperCase();
}
/**
* byte数组转换为HexString
*
* @param b
* @return
*/
public static String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (int i = 0; i < b.length; i++) {
int v = b[i] & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
/**
* @param srcJson
* @param signName
* @param signKey
* @return
* @throws Exception
*/
public static String jsonSignByHmacMD5(String srcJson, String signName, String signKey) throws Exception {
srcJson = JsonSorted.sorted(srcJson);
// System.out.println(srcJson);
JSONObject jsonObject = JSON.parseObject(srcJson);
jsonObject.put(signName, hmacMD5(srcJson, signKey));
return JsonSorted.sorted(jsonObject.toJSONString());
}
/**
* @param signJson
* @param signName
* @param signKey
* @return
* @throws Exception
*/
public static boolean verifyJsonSignByHmacMD5(String signJson, String signName, String signKey) throws Exception {
JSONObject jsonObject = JSON.parseObject(signJson);
String signValue = jsonObject.getString(signName);
if (signValue == null) {
return false;
}
jsonObject.remove(signName);
String targetJson = JsonSorted.sorted(jsonObject.toJSONString());
String signCode = hmacMD5(targetJson, signKey);
if (signValue.equalsIgnoreCase(signCode)) {
return true;
}
return false;
}
/**
* @param srcJson
* @param signName
* @param signKey
* @return
* @throws Exception
*/
public static String jsonSignByHmacSHA256(String srcJson, String signName, String signKey) throws Exception {
srcJson = JsonSorted.sorted(srcJson);
JSONObject jsonObject = JSON.parseObject(srcJson);
jsonObject.put(signName, hmacSHA256(srcJson, signKey));
return JsonSorted.sorted(jsonObject.toJSONString());
}
/**
* @param signJson
* @param signName
* @param signKey
* @return
* @throws Exception
*/
public static boolean verifyJsonSignByHmacSHA256(String signJson, String signName, String signKey) throws Exception {
JSONObject jsonObject = JSON.parseObject(signJson);
String signValue = jsonObject.getString(signName);
if (signValue == null) {
return false;
}
jsonObject.remove(signName);
String targetJson = JsonSorted.sorted(jsonObject.toJSONString());
String signCode = hmacSHA256(targetJson, signKey);
if (signValue.equalsIgnoreCase(signCode)) {
return true;
}
return false;
}
}
package com.what21.apps.json.sign;
import com.google.gson.*;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class JsonSorted {
/**
* 定义比较规则
*
* @return
*/
private static Comparator<String> getComparator() {
return (s1, s2) -> s1.compareTo(s2);
}
/**
* 排序
*
* @param e
*/
private static void sort(JsonElement e) {
if (e.isJsonNull() || e.isJsonPrimitive()) {
return;
}
if (e.isJsonArray()) {
JsonArray a = e.getAsJsonArray();
Iterator<JsonElement> it = a.iterator();
it.forEachRemaining(i -> sort(i));
return;
}
if (e.isJsonObject()) {
Map<String, JsonElement> tm = new TreeMap<>(getComparator());
for (Map.Entry<String, JsonElement> en : e.getAsJsonObject().entrySet()) {
tm.put(en.getKey(), en.getValue());
}
String key;
JsonElement val;
for (Map.Entry<String, JsonElement> en : tm.entrySet()) {
key = en.getKey();
val = en.getValue();
e.getAsJsonObject().remove(key);
e.getAsJsonObject().add(key, val);
sort(val);
}
}
}
/**
* 根据json key排序
*
* @param json
* @return
*/
public static String sorted(String json) {
// Gson gson = new GsonBuilder().setPrettyPrinting().create();
Gson gson = new GsonBuilder().create();
JsonParser p = new JsonParser();
JsonElement e = p.parse(json);
sort(e);
return gson.toJson(e);
}
}
package com.what21.apps.json.sign;
import com.google.gson.Gson;
import lombok.Data;
import java.util.Arrays;
import java.util.List;
public class JSONSignToolsTest {
@Data
static class Person {
private Integer id;
private String name;
private Integer age;
private List<String> item;
}
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setId(1);
person.setName("孙叔敖");
person.setAge(12);
person.setItem(Arrays.asList("11", "22", "33"));
Gson gson = new Gson();
String srcJson = gson.toJson(person);
String signName = "sign";
String signKey = "communicationKey";
System.out.println("源JSON:" + srcJson);
// 生成签名
String hmacMD5SignJson = JSONSignTools.jsonSignByHmacMD5(srcJson,signName,signKey);
System.out.println("HmacMD5.签名JSON: " + hmacMD5SignJson);
String hmacSHA256SignJson = JSONSignTools.jsonSignByHmacSHA256(srcJson,signName,signKey);
System.out.println("HmacSHA256.签名JSON: " + hmacSHA256SignJson);
// 验证签名
boolean result1 = JSONSignTools.verifyJsonSignByHmacMD5(hmacMD5SignJson,signName,signKey);
System.out.println("HmacMD5.签名JSON验证: " + result1);
boolean result2 = JSONSignTools.verifyJsonSignByHmacSHA256(hmacSHA256SignJson,signName,signKey);
System.out.println("HmacSHA256.签名JSON验证: " + result2);
}
}