package com.walker.infrastructure.utils; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.codec.digest.DigestUtils; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SignatureException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 功能:支付宝MD5签名处理核心文件,不需要修改 * 版本:3.3 * 修改日期:2012-08-17 * 说明: * 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。 * 该代码仅供学习和研究支付宝接口使用,只是提供一个 * */ public class MD5 { /** * MD5 32位转大写 * @param plainText * 明文 * @return 32位密文 */ public static String encryption(String plainText) { String re_md5 = StringUtils.EMPTY_STRING; try { MessageDigest md = MessageDigest.getInstance(MD5_NAME); md.update(plainText.getBytes()); byte b[] = md.digest(); int i; StringBuilder buf = new StringBuilder(StringUtils.EMPTY_STRING); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } re_md5 = buf.toString().toUpperCase(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("未找到MD5算法:" + e.getMessage(), e); } return re_md5; } /** * 签名字符串 * @param text 需要签名的字符串 * @param key 密钥 * @param input_charset 编码格式 * @return 签名结果 */ public static String sign(String text, String key, String input_charset) { text = text + key; return DigestUtils.md5Hex(getContentBytes(text, input_charset)); } /** * 签名字符串 * @param text 需要签名的字符串 * @param sign 签名结果 * @param key 密钥 * @param input_charset 编码格式 * @return 签名结果 */ public static boolean verify(String text, String sign, String key, String input_charset) { text = text + key; String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)); if(mysign.equals(sign)) { return true; } else { return false; } } /** * @param content * @param charset * @return * @throws SignatureException * @throws UnsupportedEncodingException */ private static byte[] getContentBytes(String content, String charset) { if (charset == null || StringUtils.EMPTY_STRING.equals(charset)) { return content.getBytes(); } try { return content.getBytes(charset); } catch (UnsupportedEncodingException e) { throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset); } } public static final char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public static final String MD5_NAME = "MD5"; /** * 生成MD5摘要信息,目前该方法微信支付使用 * @param buffer * @return */ public final static String getMessageDigest(byte[] buffer) { try { MessageDigest mdTemp = MessageDigest.getInstance(MD5_NAME); mdTemp.update(buffer); byte[] md = mdTemp.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 签名json数据 * @param json 输入数据 * @param key 给定的key * @return 返回签名后的字符串 */ // @Deprecated // public static String signJson(JSONObject json, String key){ // if(json == null){ // return null; // } // //过滤空值、sign与sign_type参数 // Map sParaNew = paraFilter((Map)json); // //获取待签名字符串 // String preSignStr = createLinkString(sParaNew); // return MD5.sign(preSignStr, key, StringUtils.DEFAULT_CHARSET_UTF8); // } @Deprecated public static String signJson(String json, String key){ if(json == null){ return null; } //过滤空值、sign与sign_type参数 try { Map map = JsonUtils.jsonStringToObject(json, Map.class); Map sParaNew = paraFilter(map); //获取待签名字符串 String preSignStr = createLinkString(sParaNew); return MD5.sign(preSignStr, key, StringUtils.DEFAULT_CHARSET_UTF8); } catch (Exception e) { throw new RuntimeException(e); } } /** * 快速生成签名,通过fastjson直接生成序列化字符串,它可以按照a~z字母排序。 * @param json * @param key * @return */ // public static String signJsonFast(JSONObject json, String key){ // if(json == null){ // return null; // } // return MD5.sign(json.toJSONString(), key, StringUtils.DEFAULT_CHARSET_UTF8); // } public static String signJsonFast(String json, String key){ if(StringUtils.isEmpty(json)){ return null; } return MD5.sign(json, key, StringUtils.DEFAULT_CHARSET_UTF8); } /** * 验证签名过的json数据 * @param json 待验证的json * @param sign 签名过的数据 * @param md5Key 给定的key * @return 返回结果,true正确,false失败 */ // @Deprecated // public static boolean verifyJson(JSONObject json, String sign, String md5Key){ // if(json == null){ // return false; // } // return getMd5SignVeryfy((Map)json, sign, md5Key); // } @Deprecated public static boolean verifyJson(String json, String sign, String md5Key){ if(StringUtils.isEmpty(json)){ return false; } try { Map map = JsonUtils.jsonStringToObject(json, Map.class); return getMd5SignVeryfy(map, sign, md5Key); } catch (Exception e) { throw new RuntimeException("verifyJson:转换json字符串为Map出现异常:"+e.getMessage(), e); } } /** * 替换verifyJson方法。 * 使用fastjson的自动排序功能,就能直接生成可用签名的字符串,无需考虑key的顺序问题,但fastjson版本必须1.2.5+ * @date 2019-03-07 * @param json * @param sign * @param md5Key * @return */ // public static boolean verifyJsonFast(JSONObject json, String sign, String md5Key){ // if(json == null){ // return false; // } // json.remove(NAME_SIGN_2); // return MD5.verify(json.toJSONString(), sign, md5Key, StringUtils.DEFAULT_CHARSET_UTF8); // } public static boolean verifyJsonFast(String json, String sign, String md5Key){ if(json == null){ return false; } try { ObjectNode objectNode = JsonUtils.jsonStringToObjectNode(json); objectNode.remove(NAME_SIGN_2); return MD5.verify(JsonUtils.objectToJsonString(objectNode), sign, md5Key, StringUtils.DEFAULT_CHARSET_UTF8); } catch (Exception e) { throw new RuntimeException("verifyJsonFast:转换json字符串为对象异常:" + e.getMessage(), e); } } /** * 根据反馈回来的信息,生成签名结果 * @param Params 通知返回来的参数数组 * @param sign 比对的签名结果 * @return 生成的签名结果 */ public static boolean getMd5SignVeryfy(Map Params, String sign, String md5Key) { //过滤空值、sign与sign_type参数 Map sParaNew = paraFilter(Params); //获取待签名字符串 String preSignStr = createLinkString(sParaNew); //获得签名验证结果 return MD5.verify(preSignStr, sign, md5Key, StringUtils.DEFAULT_CHARSET_UTF8); } /** * 除去数组中的空值和签名参数 * @param sArray 签名参数组 * @return 去掉空值与签名参数后的新签名参数组 */ private static Map paraFilter(Map sArray) { Map result = new HashMap(); if (sArray == null || sArray.size() <= 0) { return result; } String value = null; for (String key : sArray.keySet()) { value = sArray.get(key).toString(); if (StringUtils.isEmpty(value) || key.equalsIgnoreCase(NAME_SIGN_2) || key.equalsIgnoreCase(NAME_SIGN_TYPE)) { continue; } result.put(key, value); } return result; } /** * 把数组所有元素,并按照“参数=参数值”的模式用“&”字符拼接成字符串 * @param params 需要参与字符拼接的参数组 * @return 拼接后字符串 */ public static String createLinkString(Map params) { List keys = new ArrayList(params.keySet()); Collections.sort(keys); StringBuilder result = new StringBuilder(); String key = null; String value = null; for (int i = 0; i < keys.size(); i++) { key = keys.get(i); value = params.get(key); if(i > 0){ result.append(StringUtils.CHAR_AND); } result.append(key).append(StringUtils.CHAR_EQUALS).append(value); } return result.toString(); } public static final String NAME_SIGN_2 = "sign"; private static final String NAME_SIGN_TYPE = "sign_type"; public static void main(String[] args){ // String key = "123456"; // JSONObject input = new JSONObject(); // input.put("name", "Mike"); // input.put("age", 30); // input.put("amount", 600.2); // // String signStr = signJson(input, key); // System.out.println("签名内容 = " + signStr); // System.out.println("验证签名 = " + verifyJson(input, signStr, key)); //签名内容 = 3d4d18461ebd93e20f8d73c888b94ee9 //验证签名 = true System.out.println(MD5.encryption("123456")); } }