package wx.wechat.common.signature; import org.xml.sax.SAXException; import wx.csba.shared.util.codec.MD5; import wx.csba.shared.util.codec.SHA1; import wx.wechat.common.Configure; import wx.wechat.utils.XMLUtils; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; /** * User: rizenguo * Date: 2014/10/29 * Time: 15:23 */ public class Signature { /** * @region 以下签名算法用于微信公众号管理 */ public static String getSign4MP(Map<String, Object> map) { ArrayList<String> list = new ArrayList<String>(); for (Map.Entry<String, Object> entry : map.entrySet()) { if (entry.getValue() != "") { list.add(entry.getKey() + "=" + entry.getValue() + "&"); } } int size = list.size(); String[] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(); for (int i = 0; i < size; i++) { sb.append(arrayToSort[i]); } String result = sb.toString(); //移除最后一个& result = result.substring(0, result.length() - 1); result = SHA1.encode(result); return result; } /** * @region 以下为用于微信支付的签名计算 */ /** * 签名算法 * * @param o 要参与签名的数据对象 * @return 签名 * @throws IllegalAccessException */ // public static String getSign4Pay(Object o) throws IllegalAccessException { // ArrayList<String> list = new ArrayList<String>(); // Class cls = o.getClass(); // Field[] fields = cls.getDeclaredFields(); // for (Field f : fields) { // f.setAccessible(true); // if (f.get(o) != null && f.get(o) != "") { // list.add(f.getName() + "=" + f.get(o) + "&"); // } // } // int size = list.size(); // String[] arrayToSort = list.toArray(new String[size]); // Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); // StringBuilder sb = new StringBuilder(); // for (int i = 0; i < size; i++) { // sb.append(arrayToSort[i]); // } // String result = sb.toString(); // result += "key=" + Configure.mchKey; // result = MD5.MD5Encode(result).toUpperCase(); // return result; // } /** * @param map * @return * @function 从Map中获取签名 * 假设传送的参数如下: * <p> * appid: wxd930ea5d5a258f4f * <p> * mch_id: 10000100 * <p> * device_info: 1000 * <p> * body: test * <p> * nonce_str: ibuaiVcKdpRxkhJA * <p> * 第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下: * <p> * stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA"; * <p> * 第二步:拼接API密钥: * <p> * stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d" * <p> * sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7" * <p> * 最终得到最终发送的数据: * <p> * <xml> * <p> * <appid>wxd930ea5d5a258f4f</appid> * <p> * <mch_id>10000100</mch_id> * <p> * <device_info>1000<device_info> * <p> * <body>test</body> * <p> * <nonce_str>ibuaiVcKdpRxkhJA</nonce_str> * <p> * <sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign> * <p> * <xml> */ public static String getSign4Pay(Map<String, Object> map) { ArrayList<String> list = new ArrayList<String>(); for (Map.Entry<String, Object> entry : map.entrySet()) { if (entry.getValue() != "") { list.add(entry.getKey() + "=" + entry.getValue() + "&"); } } int size = list.size(); String[] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(); for (int i = 0; i < size; i++) { sb.append(arrayToSort[i]); } String result = sb.toString(); result += "key=" + Configure.mchKey; //Util.log("Sign Before wx.csba.shared.util.codec.MD5:" + result); result = MD5.MD5Encode(result).toUpperCase(); //Util.log("Sign Result:" + result); return result; } /** * 从API返回的XML数据里面重新计算一次签名 * * @param responseString API返回的XML数据 * @return 新鲜出炉的签名 * @throws ParserConfigurationException * @throws IOException * @throws SAXException */ public static String getSignFromResponseString(String responseString) throws IOException, SAXException, ParserConfigurationException { Map<String, Object> map = XMLUtils.XML2Map(responseString); //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名 map.put("sign", ""); //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较 return Signature.getSign4Pay(map); } /** * 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改 * * @param responseString API返回的XML数据字符串 * @return API签名是否合法 * @throws ParserConfigurationException * @throws IOException * @throws SAXException */ public static boolean checkIsSignValidFromResponseString(String responseString) throws ParserConfigurationException, IOException, SAXException { Map<String, Object> map = XMLUtils.XML2Map(responseString); String signFromAPIResponse = map.get("sign").toString(); if (signFromAPIResponse == "" || signFromAPIResponse == null) { return false; } //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名 map.put("sign", ""); //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较 String signForAPIResponse = Signature.getSign4Pay(map); if (!signForAPIResponse.equals(signFromAPIResponse)) { //签名验不过,表示这个API返回的数据有可能已经被篡改了 return false; } return true; } }