防篡改介绍详见支付宝验证
实现过程:
1.参数处理 去空去 null
/**
* 除去数组中的null值和签名参数
* @param sArray 签名参数组
* @return 去掉null值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("SignData")
|| key.equalsIgnoreCase("SignType")) {
continue;
}
result.put(key, value);
}
return result;
}
在这里也要去除空字符串,为什么呢?之前做一个项目对接,java的空串传到c中就变成null,导致签名验证一直不能通过。
2.参数排序并拼接成字符串
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @param partnerSecret
* @return 拼接后字符串
* @throws UnsupportedEncodingException
*/
public static byte[] createLinkString(Map<String, String> params,String partnerSecret) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
byte[] result = null;
try {
String join = prestr+partnerSecret;
result = join.getBytes("utf-8");//为了不同平台,对字符串进行utf-8编码的byte
return result;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return result;
}
}
传递的参数要包含以下参数 ,partnerId 合作方 signTimeMax请求时间 ,encrypttype 加密算法类型
除此之外双方还要约定一个密匙 如 123
假如参数去空去 null 后拼接字符串为
param1=1¶m2=2¶m3=3&signTimeMax=xxxx
这时候需将密匙拼接到字符串最后变为
param1=1¶m2=2¶m3=3&signTimeMax=xxxx123
3.做算法加密
/**
* SHA1加密
* @param data 要处理的数据
* @param key 加密算法名称
* @return
* @throws NoSuchAlgorithmException
*/
public static byte[] encryptSHA1OrMD5(byte[] data, String key) throws NoSuchAlgorithmException{
MessageDigest messageDigest = MessageDigest.getInstance(key);
messageDigest.update(data);
return messageDigest.digest();
}
这里 MessageDigest 也能加密为 MD5 。
4.将计算得来的byte[] 转成16进制
/**
* 将byte转为 HexString
* @param byteArray
* @return
*/
public static String byteArrayToHex(byte[] byteArray) {
// 首先初始化一个字符数组,用来存放每个16进制字符
char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
// new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符)
char[] resultCharArray =new char[byteArray.length * 2];
// 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
int index = 0;
for (byte b : byteArray) {
resultCharArray[index++] = hexDigits[b>>> 4 & 0xf];
resultCharArray[index++] = hexDigits[b& 0xf];
}
// 字符数组组合成字符串返回
return new String(resultCharArray);
}
这里也不一定要转成16进制,看具体需求。也有可能是 base64转字符串等等。
/**
* 将byte转为 Base64String
* @param byteArray
* @return
*/
public static String byteArrayToBase64(byte[] byteArray){
String base64Str = new String(Base64.encodeBase64(byteArray));
return base64Str;
}
最后方法的调用
/**
*
* @param data 要加密的数据
* @return signData 加密后的字符串
* @throws NoSuchAlgorithmException
*/
public static String encryptionAlgorithm(Map data,String partnerSecret,String SIGNTYPE) throws NoSuchAlgorithmException{
Map map = paraFilter(data);
byte[] signDataByte = createLinkString(map,partnerSecret);
String signData = byteArrayToHex(encryptSHA1OrMD5(signDataByte, SIGNTYPE));
return signData;
}