欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RSA 数字签名—— Java 实现

程序员文章站 2022-03-14 20:29:44
...

RSA 数字签名—— Java 实现

依赖

       <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
            <version>3.6</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>

代码

package awesome.data.structure.algorithm.encrypt.rsa;

import awesome.data.structure.algorithm.encrypt.md5.Md5Util;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * RSA 工具类
 *
 * @author: Andy
 * @time: 2019/5/10 16:50
 * @since
 */
public class RSAUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtil.class);

    private static final int RSA_SIZE_1024 = 1024;

    private static final String ALGORITHM = "SHA1WithRSA";

    /**
     * 生成 RSA **对
     *
     * @param keySize
     * @return: {@link Map<String,Object> }
     * @author: Andy
     * @time: 2019/5/10 16:59
     */
    public static Map<String, Object> createKeyPair(int keySize) {
        KeyPairGenerator ****** = null;
        try {
            ****** = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            LOGGER.error("初始化**工具异常", e);
            return null;
        }
        ******.initialize(keySize, new SecureRandom());
        KeyPair key = ******.generateKeyPair();
        PublicKey publicKey = key.getPublic();
        PrivateKey privateKey = key.getPrivate();
        Map map = new HashMap();
        map.put("publicKey", publicKey);
        map.put("privateKey", privateKey);
        map.put("publicKeyBase64", Base64.encodeBase64String(publicKey.getEncoded()));
        map.put("privateKeyBase64", Base64.encodeBase64String(privateKey.getEncoded()));
        return map;
    }

    /**
     * 获得公钥的 Base64 字符串
     *
     * @param publicKey 公钥
     * @return: {@link String }
     * @author: Andy
     * @time: 2019/5/10 17:11
     */
    public static String getBase64PublicKeyString(PublicKey publicKey) {
        return Base64.encodeBase64URLSafeString(publicKey.getEncoded()).trim();
    }

    /**
     * 获得私钥的 Base64 字符串
     *
     * @param privateKey 公钥
     * @return: {@link String }
     * @author: Andy
     * @time: 2019/5/10 17:11
     */
    public static String getBase64PrivateKeyString(PrivateKey privateKey) {
        return Base64.encodeBase64URLSafeString(privateKey.getEncoded()).trim();
    }

    /**
     * 获取公钥
     *
     *
     * @param publicKeyBase64 公钥的 Base64 字符串
     * @return: {@link PublicKey }
     * @author: Andy
     * @time: 2019/5/10 18:05
     */
    public static PublicKey getPublicKey(String publicKeyBase64)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(org.apache.commons.net.util.Base64.decodeBase64(publicKeyBase64));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
        return publicKey;
    }

    /**
     * 获取私钥
     *
     *
     * @param privateKeyBase64 私钥的 Base64 字符串
     * @return: {@link PrivateKey }
     * @author: Andy
     * @time: 2019/5/10 18:05
     */
    public static PrivateKey getPrivateKey(String privateKeyBase64)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(org.apache.commons.net.util.Base64.decodeBase64(privateKeyBase64));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey priKey = keyFactory.generatePrivate(priKeySpec);
        return priKey;
    }


    /**
     * 使用私钥对数据进行数字签名
     *
     * @param data       需要签名的数据
     * @param privateKey 私钥
     * @return: {@link byte[] }
     * @author: Andy
     * @time: 2019/5/10 17:15
     */
    public static byte[] sign(byte[] data, PrivateKey privateKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }

    /**
     * 使用私钥对数据进行数字签名
     *
     * @param data       需要签名的字符串
     * @param privateKey 私钥
     * @return: {@link String }
     * @author: Andy
     * @time: 2019/5/10 17:15
     */
    public static String sign(String data, PrivateKey privateKey)
            throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
        return Base64.encodeBase64URLSafeString(sign(data.getBytes(), privateKey)).trim();
    }

    /**
     * 签名校验
     *
     * @param data      参与签名的数据
     * @param sign      数字签名
     * @param publicKey 公钥
     * @return: {@link boolean }
     * @author: Andy
     * @time: 2019/5/10 17:22
     */
    public static boolean verify(byte[] data, byte[] sign, PublicKey publicKey)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(sign);
    }

    /**
     * 签名校验
     *
     * @param data      参与签名的数据
     * @param sign      数字签名
     * @param publicKey 公钥
     * @return: {@link boolean }
     * @author: Andy
     * @time: 2019/5/10 17:22
     */
    public static boolean verify(String data, String sign, PublicKey publicKey)
            throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
        return verify(data.getBytes(), Base64.decodeBase64(sign), publicKey);
    }

    /**
     * 获取参与签名的参数的字符串。参数拼接的顺序由 TreeMap 决定。
     *
     * @param paramsMap 参与签名的参数名和参数值的映射
     * @return: {@link String }
     * @author: Andy
     * @time: 2019/5/10 17:43
     */
    public static String getSourceSignData(TreeMap<String, String> paramsMap) {
        StringBuilder paramsBuilder = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> paramEntry : paramsMap.entrySet()) {
            if(!first){
                paramsBuilder.append("&");
            }else {
                first = false;
            }

            paramsBuilder.append(paramEntry.getKey()).append("=").append(paramEntry.getValue());
        }

        return paramsBuilder.toString();
    }


    /**
     * 设计流程:
     * <p>
     * 1、创建**对
     * 2、获取参与签名的数据
     * 3、获取参与签名的数据的摘要(MD5值)
     * 4、使用私钥对摘要进行数字签名
     * 5、使用公钥验证签名
     *
     * @author: Andy
     * @time: 2019/5/10 17:32
     */
    public static void main(String[] args) throws InvalidKeySpecException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //1、创建**对
        Map<String, Object> keyPairMap = createKeyPair(RSA_SIZE_1024);
        String publicKeyBase64 = keyPairMap.get("publicKeyBase64").toString();
        String privateKeyBase64 = keyPairMap.get("privateKeyBase64").toString();
        System.out.println(String.format("publicKeyBase64: %s", publicKeyBase64));
        System.out.println(String.format("privateKeyBase64: %s", privateKeyBase64));

        //2、获取参与签名的数据
        //创建 TreeMap, 排序方式为字符串的自然顺序
        TreeMap<String, String> paramsMap = new TreeMap<>();
        paramsMap.put("token", "token");
        paramsMap.put("batchNo", "批次号");
        paramsMap.put("totalBillCount", "1");
        paramsMap.put("sumMoney", "1");
        paramsMap.put("idCard", "452626199212070014");
        paramsMap.put("name", "张三");
        paramsMap.put("afterTaxMoney", "1");
        paramsMap.put("remark", "备注");
        String sourceSignData = getSourceSignData(paramsMap);
        System.out.println(String.format("参与签名的数据: %s", sourceSignData));

        //3、获取参与签名的数据的摘要(MD5值)
        String md5Str = Md5Util.encrypt(sourceSignData);
        System.out.println(String.format("参与签名的数据的摘要(MD5值): %s", md5Str));

        //4、使用私钥对摘要进行数字签名
        String signature = sign(md5Str, getPrivateKey(privateKeyBase64));
        System.out.println(String.format("数字签名: %s", signature));

        //5、使用公钥验证签名
        boolean success = verify(md5Str, signature, getPublicKey(publicKeyBase64));

        System.out.println(String.format("签名验证结果:%s", success));

    }



}

package awesome.data.structure.algorithm.encrypt.md5;


import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Md5 工具类
 *
 * @author: Andy
 * @time: 2019/5/10 17:27
 * @since
 */
public class Md5Util {
    private static final Logger LOGGER = LoggerFactory.getLogger(Md5Util.class);

    /**
     * 简单 MD5 加密
     *
     * @param str
     * @return: {@link String }
     * @author: Andy
     * @time: 2019/4/29 15:28
     * @since
     */
    public static String encrypt(String str) {
        try {
            return DigestUtils.md5Hex(str).toUpperCase();
        } catch (Exception e) {
            LOGGER.error("MD5加密失败!", e);
        }
        return null;
    }
}


运行结果

publicKeyBase64: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMcizy+1sAprsTLKyk8DLU8yR29ZMuol7T50LeyG4JMQxewXm05g56UlVg1cx8FAJCrMUg6ZU8KyjJycP+au9mkGsRUZv2k5Pwahhmt9Y2yviuIDUuGJ3/4ojgvaqsxSE+yQi+rLconS5DxwOOTJ0Dp5nG7eo5auVCkk8+uy7JlwIDAQAB
privateKeyBase64: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIxyLPL7WwCmuxMsrKTwMtTzJHb1ky6iXtPnQt7IbgkxDF7BebTmDnpSVWDVzHwUAkKsxSDplTwrKMnJw/5q72aQaxFRm/aTk/BqGGa31jbK+K4gNS4Ynf/iiOC9qqzFIT7JCL6styidLkPHA45MnQOnmcbt6jlq5UKSTz67LsmXAgMBAAECgYB+KbKnM5S0KRK0TtVn9T40fZasJj5pDgMRaBVx+6qdJyptlG+4SVGIIJ4Btw1SCMdfDcSnpC0jN8IUQuOFkJosu7N6iB4IxEIe7cDOWppO5fOAR51c9tU+Z6544RdEcL9+X4S2GfdT9IObriMrcg0q0PSnj73MpkKJ7jV0ybWNQQJBAL3AnHjgxFMYFInoagQ0yX1KMSQlv+OrbQbQoZYGsvaacRcVKa0wngeeF3lzFN2XxoMqUR1e5gbMiHJMTdjPKw0CQQC9erxjde5NYJtGtPYTx6bXL3zDKI/T82uqlCt3lMjTe+s73q9oUPFvBU0Zj/IWlqD0kgB1NETMJid1WPT+KI4zAkAdmKNPP0+f3kulzvhqO4mJ3z6W8sRhUGWrAHOToOvdBu3IueZMOx8K9R+YM9j3TysJXlpUiG68dL57hWdG/9kpAkBVXLcEg/uw5mXt69a0wIx8g0tMzLhHP3Hw7kHK5L+47ynh1gfmEhlC7/t1GbFx1bh7lk8YfkzEQCgkqMfuvKybAkAnkLilyXl60wLfeinuynte1IQGk5kNuH0/DaP89UkWnXIEfH4FBIXETIhSRXHYnDMEGfojuE3J25QZw2eKY8rq
参与签名的数据: afterTaxMoney=1&batchNo=批次号&idCard=452626199212070014&name=张三&remark=备注&sumMoney=1&token=token&totalBillCount=1
参与签名的数据的摘要(MD5值): 553218F911324C5F45282142C036778F
数字签名: inrW4yFyEZrq1RNMVVaca5ImPiudcRsoCtlJCu8YADZ6mBLI5pHd9MkCElxNJCxFMbraEbXA8blhMbvdt3fqcgnJADIcy0xPVLLNXubCDes-brgmMBcmRLoEAFR_KZrDxj_5QzGZUekkiQxNh8_2emBxqvXjhUCJr-uwAzuwP9A
签名验证结果:true
相关标签: RSA 数字签名