数字签名
<h2>签名</h2>
<p>平台通过验证数字签名来保证请求的真实性和数据的完整性。</p>
<h3>请求签名</h3>
<p>应用需要使用平台分配的签名key以MD5的方式对消息体进行签名。请求的签名信息通过参数<code>sign</code> 传递。没有携带签名或者签名验证不通过的请求,都不会被执行,并返回<code>401 Unauthorized</code> 。</p>
<h3>应答签名</h3>
<p>对于签名验证成功的请求,平台同样会使用对应的签名key以MD5的方式对应答进行签名。签名的信息包含响应参数<code>sign</code>里。</p>
<p>没有携带签名的成功应答(HTTP状态码为2xx),应认为是伪造或被篡改的应答。</p>
<h3>签名规则</h3>
<p>(1) 将原始的请求参数(或响应参数)列表,排除掉不包含值的参数和sign参数。
(2) 对参数名称按照升序排序,然后依次拼接参数值,最后在尾部拼接上签名key,得到签名原串plainText。
(3) 对plainText做MD5得到摘要字节数组,对字节数组做十六进制转码,得到字符串,作为签名参数<code>sign</code>的值。</p>
<p><code>注:如果包含敏感数据,例如密码、手机号,会先加密,再做生成签名的处理</code></p>
<h3>签名示例</h3>
<pre><code>import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
import java.util.*;
public class MD5SignUtils {
private static final Logger LOG = LoggerFactory.getLogger(MD5SignUtils.class);
/**
* 获取签名
*
* @param param
* @return
*/
public static String getSign(String md5Key, Map<String, String> param) {
try {
return _md5Encode(getSignPlainText(md5Key, param));
} catch (Exception e) {
LOG.error("签名错误", e);
return "ERROR";
}
}
/**
* 验签
*
* @param signKey
* @param param
* @return
*/
public static boolean verifySign(String signKey, Map<String, String> param) {
String sign = param.get("sign");
param.remove("sign");
String verifySign = getSign(signKey, param);
if (verifySign.equals(sign)) {
LOG.info("验签通过");
return true;
} else {
LOG.info("验签失败");
return false;
}
}
public static String getSignPlainText(String md5Key, Map<String, String> param) {
try {
if (param.get("sign") != null) {
param.remove("sign");
}
List arrayList = new ArrayList(param.entrySet());
Collections.sort(arrayList, new Comparator() {
public int compare(Object o1, Object o2) {
Map.Entry obj1 = (Map.Entry) o1;
Map.Entry obj2 = (Map.Entry) o2;
return (obj1.getKey()).toString().compareTo((String) obj2.getKey());
}
});
StringBuilder md5keyBuilder = new StringBuilder("");
for (Iterator iter = arrayList.iterator(); iter.hasNext(); ) {
Map.Entry entry = (Map.Entry) iter.next();
String value = String.valueOf(entry.getValue());
if (StringUtils.isEmpty(value) || value.equals("null")) {
continue;
}
md5keyBuilder.append(value);
}
md5keyBuilder.append(md5Key);
// LOG.info("签名参数:" + md5key);
return md5keyBuilder.toString();
} catch (Exception e) {
LOG.error("组装签名参数错误", e);
return "ERROR";
}
}
/**
* MD5加密 生成32位md5码
*
* @param inStr 待加密字符串
* @return 返回32位md5码
*/
public static String _md5Encode(String inStr) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] byteArray = inStr.getBytes("UTF-8");
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
}</code></pre>