Java使用RSA加密和解密,使用SHA1withRSA算法进行 签名和验签

package util;

import cn.hutool.core.codec.Base64;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA工具类.
 *
 * @author zsh
 * @version 1.0
 * @date 2022/12/14 17:50
 */
@Slf4j
@UtilityClass
public class RSAUtil {
    
    /**
     * 加密算法RSA.
     */
    private static final String KEY_ALGORITHM = "RSA";
    
    /**
     * 签名算法.
     */
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
    
    /**
     * 密钥长度,DH算法的默认密钥长度是1024 密钥长度必须是64的倍数,在512到65536位之间.
     **/
    private static final int KEY_SIZE = 1024;
    
    /**
     * 随机生成密钥对.
     *
     * @return
     */
    public static Map<String, String> genKeyPair() {
        try {
            // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 初始化密钥对生成器,密钥大小为96-1024位
            keyPairGen.initialize(KEY_SIZE);
            // 生成一个密钥对,保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            // 得到私钥
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            // 得到公钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            // 得到公钥字符串
            String publicKeyString = Base64.encode(publicKey.getEncoded());
            // 得到私钥字符串
            String privateKeyString = Base64.encode(privateKey.getEncoded());
            Map<String, String> map = new HashMap<>();
            map.put("publicKey", publicKeyString);
            map.put("privateKey", privateKeyString);
            return map;
        } catch (Exception e) {
            log.error("生成密钥对异常", e);
        }
        return null;
    }
    
    /**
     * 公钥加密过程.
     *
     * @param publicKeyStr  公钥
     * @param plainTextData 明文数据
     * @return
     */
    public static byte[] encryptByPublicKey(String publicKeyStr, String plainTextData) throws Exception {
        if (StringUtil.isBlank(publicKeyStr)) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            return encryptByPublicKey(publicKeyStr, plainTextData.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("公钥加密异常", e);
            throw new Exception("公钥加密异常");
        }
    }
    
    /**
     * 公钥加密过程.
     *
     * @param publicKeyStr  公钥
     * @param plainTextData 明文数据
     * @return
     */
    public static byte[] encryptByPublicKey(String publicKeyStr, byte[] plainTextData) throws Exception {
        if (StringUtil.isBlank(publicKeyStr)) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            return encrypt(loadPublicKeyByStr(publicKeyStr), plainTextData);
        } catch (Exception e) {
            log.error("公钥加密异常", e);
            throw new Exception("公钥加密异常");
        }
    }
    
    /**
     * 公钥加密过程.
     *
     * @param publicKey     公钥
     * @param plainTextData 明文数据
     * @return
     */
    public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            // 使用默认RSA
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return processData(cipher, plainTextData, KEY_SIZE / 8 - 11);
        } catch (Exception e) {
            log.error("公钥加密异常", e);
            throw new Exception("公钥加密异常");
        }
    }
    
    /**
     * 私钥解密过程.
     *
     * @param privateKeyStr 私钥
     * @param baseData      密文数据(base64加密)
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decryptByPrivateKey(String privateKeyStr, String baseData) throws Exception {
        if (StringUtil.isBlank(privateKeyStr)) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            return decryptByPrivateKey(privateKeyStr, Base64.decode(baseData));
        } catch (Exception e) {
            log.error("私钥解密异常", e);
            throw new Exception("私钥解密异常");
        }
    }
    
    /**
     * 私钥解密过程.
     *
     * @param privateKeyStr 私钥
     * @param cipherData    密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decryptByPrivateKey(String privateKeyStr, byte[] cipherData) throws Exception {
        if (StringUtil.isBlank(privateKeyStr)) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            // 获取私钥
            RSAPrivateKey privateKey = loadPrivateKeyByStr(privateKeyStr);
            return decrypt(privateKey, cipherData);
        } catch (Exception e) {
            log.error("私钥解密异常", e);
            throw new Exception("私钥解密异常");
        }
    }
    
    /**
     * 私钥解密过程.
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            // 使用默认RSA
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return processData(cipher, cipherData, KEY_SIZE / 8);
        } catch (Exception e) {
            log.error("私钥解密异常", e);
            throw new Exception("私钥解密异常");
        }
    }
    
    /**
     * RSA签名.
     *
     * @param content    待签名数据
     * @param privateKey 商户私钥
     * @return 签名值
     */
    public static String sign(byte[] content, String privateKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(loadPrivateKeyByStr(privateKey));
            signature.update(content);
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            log.error("生成签名异常", e);
        }
        return null;
    }
    
    /**
     * RSA签名.
     *
     * @param content    待签名数据(base64)
     * @param privateKey 商户私钥
     * @return 签名值
     */
    public static String sign(String content, String privateKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(loadPrivateKeyByStr(privateKey));
            signature.update(Base64.decode(content));
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            log.error("生成签名异常", e);
        }
        return null;
    }
    
    /**
     * RSA验签名检查.
     *
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 分配给开发商公钥
     * @return 布尔值
     */
    public static boolean doCheck(byte[] content, String sign, String publicKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(loadPublicKeyByStr(publicKey));
            signature.update(content);
            return signature.verify(Base64.decode(sign));
        } catch (Exception e) {
            log.error("验证签名异常", e);
        }
        return false;
    }
    
    /**
     * RSA验签名检查.
     *
     * @param content   待签名数据(base64)
     * @param sign      签名值
     * @param publicKey 分配给开发商公钥
     * @return 布尔值
     */
    public static boolean doCheck(String content, String sign, String publicKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(loadPublicKeyByStr(publicKey));
            signature.update(Base64.decode(content));
            return signature.verify(Base64.decode(sign));
        } catch (Exception e) {
            log.error("验证签名异常", e);
        }
        return false;
    }
    
    /**
     * 从字符串中加载公钥.
     *
     * @param publicKeyStr 公钥数据字符串
     * @return
     */
    private RSAPublicKey loadPublicKeyByStr(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            log.error("公钥异常", e);
            throw new Exception("公钥异常");
        }
    }
    
    /**
     * 获取 私钥.
     *
     * @param privateKeyStr 私钥数据字符串
     * @throws Exception 加载私钥时产生的异常
     */
    private RSAPrivateKey loadPrivateKeyByStr(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            log.error("私钥异常", e);
            throw new Exception("私钥异常");
        }
    }
    
    /**
     * 分段处理数据.
     *
     * @param cipher      密码算法
     * @param dataes      数据
     * @param segmentSize 分段大小(小于等于0不分段)
     * @return
     */
    private byte[] processData(Cipher cipher, byte[] dataes, int segmentSize) {
        byte[] decBytes = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int inputLength = dataes.length;
            int offSet = 0;
            for (int i = 0; inputLength - offSet > 0; offSet = i * segmentSize) {
                byte[] cache;
                if (inputLength - offSet > segmentSize) {
                    cache = cipher.doFinal(dataes, offSet, segmentSize);
                } else {
                    cache = cipher.doFinal(dataes, offSet, inputLength - offSet);
                }
                out.write(cache, 0, cache.length);
                ++i;
            }
            decBytes = out.toByteArray();
            out.close();
        } catch (Exception e) {
            log.error("分段处理数据异常", e);
        }
        return decBytes;
    }
    
    public static void main(String[] args) throws Exception {
        // 请求端
        Map<String, String> genKeyPair = genKeyPair();
        // 请求端公钥
        String publicKey = genKeyPair.get("publicKey");
        // 请求端私钥
        String privateKey = genKeyPair.get("privateKey");
        
        // 接收端
        Map<String, String> keyPair = genKeyPair();
        // 接收端公钥
        String toPublicKey = keyPair.get("publicKey");
        // 接收端私钥
        String toPrivateKey = keyPair.get("privateKey");
        
        // -------------------------发起请求----------------------------
        // 请求数据 进行数据加密 加签名
        Map<String, Object> map = new HashMap<>();
        map.put("username", "zsh");
        map.put("age", 35);
        String plainText = JsonUtils.toJsonString(map);
        System.out.println("加密前数据:" + plainText);
        
        // 接收端 公钥加密
        byte[] encrypt = encryptByPublicKey(toPublicKey, plainText.getBytes(StandardCharsets.UTF_8));
        System.out.println("base64加密之前数据:" + Arrays.toString(encrypt));
        // base64加密
        String data = Base64.encode(encrypt);
        System.out.println("base64加密之后数据:" + data);
        // 签名 私钥
        String sign = sign(data, privateKey);
        System.out.println("加密签名:" + sign);
        
        // --------------------------收到请求---------------------------
        // 收到加密数据
        // 验签 请求端 公钥
        String content = Arrays.toString(Base64.decode(data));
        System.out.println("base64解密之后数据:" + content);
        
        // 请求端 公钥
        // 验签
        boolean doCheck = doCheck(data, sign, publicKey);
        System.out.println("验签:" + doCheck);
        
        // 接收端 公钥解密
        // 私钥
        byte[] decrypt = decryptByPrivateKey(toPrivateKey, data);
        System.out.println("解密后数据:" + new String(decrypt));
    }
}