签名机制把需要参与签名的参数放入数组中,对数组里的每一个参与签名的参数从a到z的顺序排序,若遇到相同首字母,则看第二个字母,以此类推。排序完成之后,再把所有数组值以“&”字符连接起来,这串字符串便是待签名字符串。例如:
http://www.yoodb.com?id=79&return_url=http%3A%2F%2Fwww.baidu.com&sign=B7A7D78E1B8693B032EEFE7E8A8A5B1B。
import java.security.MessageDigest; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; public class SignUtil { /** * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。 */ public String createSign(SortedMap<String, String> packageParams) throws Exception { StringBuffer sb = new StringBuffer(); Set<?> es = packageParams.entrySet(); Iterator<?> it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); String v = (String) entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); String sign = MD5Encode(sb.toString()).toUpperCase(); return sign; } /** * MD5编码 * @param origin 原始字符串 * @return 经过MD5加密之后的结果 */ private static String MD5Encode(String origin) { String resultString = null; try { resultString = origin; MessageDigest md = MessageDigest.getInstance("MD5"); md.update(resultString.getBytes("UTF-8")); resultString = byteArrayToHexString(md.digest()); } catch (Exception e) { e.printStackTrace(); } return resultString; } /** * 转换字节数组为16进制字串 * @param b 字节数组 * @return 16进制字串 */ private static String byteArrayToHexString(byte[] b) { StringBuilder resultSb = new StringBuilder(); for (byte aB : b) { resultSb.append(byteToHexString(aB)); } return resultSb.toString(); } /** * 转换byte到16进制 * @param b 要转换的byte * @return 16进制格式 */ private static String byteToHexString(byte b) { int n = b; if (n < 0) { n = 256 + n; } int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; public static void main(String[] args) throws Exception { SortedMap<String, String> packageParams = new TreeMap<String, String>(); packageParams.put("id", "XXXXXXXXXXXXXXXXXXXX"); packageParams.put("return_url", "http://www.baidu.com"); String str = new SignUtil().createSign(packageParams); System.out.println(str); } }
根据HTTP协议要求,传递参数的值中如果存在特殊字符(如:&、@等),那么该值需要做URL Encoding,这样请求接收方才能接收到正确的参数值。这种情况下,待签名数据应该是原生值而不是encoding之后的值。
在MD5签名时,需要安全校验码(key)参与签名,当拿到请求时的待签名字符串后,需要把安全校验码(key)直接拼接到待签名字符串后面,形成新的字符串。利用MD5的签名函数对这个新的字符串进行签名运算,从而得到32位签名结果字符串。