Java实现签名工具类

一 点睛

签名和验证签名常常用于网络安全,在此提供一个工具类。

二 代码

package com.imooc.demo.common.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.DocumentException;

import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.util.*;
import java.util.Map.Entry;

/**
 * @className: SignatureUtils
 * @description: 签名和验证签名工具类
 * @date: 2020/8/21
 * @author: cakin
 */
public class SignatureUtils {
    /**
     * 签名,MD5.
     *
     * @param paramMap 参数Map,不包含商户秘钥且顺序确定
     * @param key      商户秘钥
     * @return 签名串
     */
    public static String sign(Map<String, Object> paramMap, String key) {
        if (key == null) {
            throw new RuntimeException("key不能为空");
        }
        String sign = createSign(paramMap, key);
        return sign;
    }

    /**
     * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     *
     * @param paramMap 参数Map,不包含商户秘钥且顺序确定
     * @param key      商户秘钥
     * @return 签名串
     */
    private static String createSign(Map<String, Object> paramMap, String key) {
        StringBuffer sb = new StringBuffer();
        SortedMap<String, Object> sort = new TreeMap<String, Object>(paramMap);
        Set<Entry<String, Object>> es = sort.entrySet();
        Iterator<Entry<String, Object>> it = es.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v) && !"null".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + key);
        System.out.println("HMAC source:{" + sb.toString() + "}");
        // 签名
        String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
        System.out.println("HMAC:{" + sign + "}");
        return sign;
    }

    /**
     * 验证签名, 仅支持MD5.
     *
     * @param paramMap 参数Map,不包含商户秘钥且顺序确定
     * @param key      商户秘钥
     * @param sign     签名串
     * @return 验签结果
     */
    public static boolean checkSign(Map<String, Object> paramMap, String key, String sign) {
        if (key == null) {
            throw new RuntimeException("key不能为空");
        }
        if (sign == null) {
            throw new RuntimeException("需要验签的字符为空");
        }

        return sign.equals(sign(paramMap, key));
    }


    /**
     * 通过request获取签名Map
     *
     * @param request 请求
     * @return 签名Map
     */
    public static Map<String, Object> getSignMap(HttpServletRequest request) {
        Map<String, Object> paramMap = new HashMap<>();
        Map<String, String[]> map = request.getParameterMap();
        Set<Entry<String, String[]>> es = map.entrySet();
        Iterator<Entry<String, String[]>> it = es.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry) it.next();
            String k = (String) entry.getKey();
            Object ov = entry.getValue();
            String v = "";
            if (ov instanceof String[]) {
                String[] value = (String[]) ov;
                v = value[0];
            } else {
                v = ov.toString();
            }
            paramMap.put(k, v);
        }
        return paramMap;
    }

    /**
     * 通过url获取签名Map
     *
     * @param url 请求地址
     * @return 签名Map
     */
    @SuppressWarnings("deprecation")
    public static Map<String, Object> getSignMap(String url) {
        Map<String, Object> paramMap = new HashMap<>();
        url = url.substring(url.indexOf("?") + 1);
        String[] params = url.split("&");
        for (int i = 0; i < params.length; i++) {
            String param = params[i];
            if (param.indexOf("=") != -1) {
                String[] values = param.split("=");
                if (values != null && values.length == 2) {
                    //update----begin---author:scott----date:20160115----for:昵称转码,签名问题处理----
                    if ("nickname".equals(values[0])) {
                        paramMap.put(values[0], URLDecoder.decode(values[1]));
                    } else {
                        paramMap.put(values[0], values[1]);
                    }
                    //update----begin---author:scott----date:20160115----for:昵称转码,签名问题处理----
                }
            }
        }
        return paramMap;
    }

    /**
     * 从API返回的XML数据里面计算签名
     *
     * @param responseString API返回的XML数据
     * @param key            商户秘钥
     * @return 签名
     * @throws DocumentException 文档异常
     */
    public static String getSignFromResponseString(String responseString, String key) throws DocumentException {
        Map<String, Object> map = XmlUtils.xmlBody2map(responseString, "xml");
        // 清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
        map.put("sign", "");
        // 将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        return sign(map, key);
    }

    /**
     * 检验API返回的数据里面的签名是否合法,避免数据在传输的过程中被第三方篡改
     *
     * @param responseString API返回的XML数据字符串
     * @return API签名是否合法
     * @throws DocumentException 文档异常
     */
    public static boolean checkIsSignValidFromResponseString(String responseString, String key) throws DocumentException {
        Map<String, Object> map = XmlUtils.xmlBody2map(responseString, "xml");
        System.out.println(map.toString());
        Object signFromAPIResponse = map.get("sign");
        if (signFromAPIResponse == null || signFromAPIResponse == "") {
            System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
            return false;
        }
        System.out.println("服务器返回包里面的签名是:" + signFromAPIResponse);
        // 清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
        map.put("sign", "");
        // 将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
        String signForAPIResponse = sign(map, key);
        if (!signForAPIResponse.equals(signFromAPIResponse)) {
            // 签名验不过,表示这个API返回的数据有可能已经被篡改了
            System.out.println("API返回的数据签名验证不通过,有可能被第三方篡改!!!");
            return false;
        }
        System.out.println("恭喜,API返回的数据签名验证通过!!!");
        return true;
    }

    /**
     * 除去Map中的空值和签名参数
     *
     * @param sArray 含有签名的Map
     * @return 去掉空值与签名参数后的新签名Map
     */
    public static Map<String, String> filter(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 (StringUtils.isEmpty(value) || key.equalsIgnoreCase("sign")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的Map
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        // 第一步:把字典按Key的字母顺序排序,参数使用TreeMap已经完成排序
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        // 第二步:把所有参数名和参数值串在一起
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (!StringUtils.isEmpty(value)) {
                sb.append(key).append("=").append(value);
            }
        }
        return sb.toString();
    }


    public static void main(String[] args) {
        System.out.println("------------------------测试从url中提取map---------------------");
        String url = "http://www.saphao.com:9999/P3-Web/commonxrs/toIndex.do?actId=402880ee51334a520151334c3eaf0001&openid=oR0jFt_DTsAUJebWqGeq3A1VWfRw&nickname=JEFF&subscribe=1&jwid=&sign=F5E56A64B650A98E67CCCFFF871C7133";
        Map<String, Object> t = getSignMap(url);
        for (Entry<String, Object> entry : t.entrySet()) {
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
        System.out.println("------------------------测试签名过程----------------------------");
        String key = "26F72780372E84B6CFAED6F7B19139CC47B1912B6CAED753";
        Map<String, Object> paramMap = new HashMap<>();
        //paramMap.put("id", "134");
        paramMap.put("token", "edb7442d8d1a00db28062ec711f64c40");
        //paramMap.put("smsCode", "967910");
        //paramMap.put("loginName", "17792516435");
        System.out.println(createSign(paramMap, key));
    }
}

三 测试

------------------------测试从url中提取map---------------------
subscribe--->1
openid--->oR0jFt_DTsAUJebWqGeq3A1VWfRw
actId--->402880ee51334a520151334c3eaf0001
nickname--->JEFF
sign--->F5E56A64B650A98E67CCCFFF871C7133
------------------------测试签名过程----------------------------
HMAC source:{token=edb7442d8d1a00db28062ec711f64c40&key=26F72780372E84B6CFAED6F7B19139CC47B1912B6CAED753}
HMAC:{B1F25B7D27EAF67126712F2F1EC50504}
B1F25B7D27EAF67126712F2F1EC50504

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 实现数字签名可以使用 Java Cryptography Architecture (JCA) 提供的工具类和算法。下面是一个简单的示例,演示如何使用 Java 实现数字签名: ```java import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.Signature; public class DigitalSignatureExample { public static void main(String[] args) throws Exception { // 加载密钥库 String keyStoreFile = "keystore.jks"; String keyStorePassword = "password"; String keyAlias = "alias"; String keyPassword = "password"; KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray()); // 获取私钥 PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword.toCharArray()); // 加载待签名文件 String inputFile = "input.pdf"; byte[] inputBytes = new byte[(int) new File(inputFile).length()]; FileInputStream input = new FileInputStream(inputFile); input.read(inputBytes); input.close(); // 使用 SHA-256 算法计算摘要 byte[] digest = MessageDigest.getInstance("SHA-256").digest(inputBytes); // 使用 RSA 算法对摘要进行签名 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(digest); byte[] signatureBytes = signature.sign(); // 输出签名结果 System.out.println("Digital signature:"); System.out.println(new String(Base64.getEncoder().encode(signatureBytes))); } } ``` 上述代码加载了一个密钥库,获取了一个私钥,并使用该私钥对一个文件进行签名签名过程包括计算文件的摘要和使用 RSA 算法对摘要进行签名。最终输出签名结果。请注意,此示例仅用于演示目的,实际使用中应该更加完善和安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值