Rsa加签验签工具

Rsa加签验签工具

为了方便对外接口加签验签,写了个通用的加签验签工具。

一、上代码

1.1 RsaUtils 代码

PRIVATE_KEY、PUBLIC_KEY 可以在线生成或者通过支付宝工具生成。
在线生成公钥私钥对,RSA公私钥生成

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.meiyuan.food.research.common.exception.AppException;
import com.meiyuan.food.research.common.utils.JsonUtils;
import com.meiyuan.food.research.module.order.dto.OrderSaveDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import java.math.BigDecimal;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
/**
 * @author zjson
 */
@Slf4j
public final class RsaUtils {

    /**
     * 签名字段
     */
    public static final String SIGN_KEY = "sign";
    private static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

    private static final String PRIVATE_KEY = "";
    private static final String PUBLIC_KEY = "";

    /**
     * 获取RSA公钥 根据钥匙字段
     *
     * @param pubKey 公钥串
     * @return 公钥
     */
    public static PublicKey getPublicKey(String pubKey) {
        if (StringUtils.isBlank(pubKey)) {
            return null;
        }
        pubKey = pubKey.replace("-----BEGIN PUBLIC KEY-----", "");
        pubKey = pubKey.replace("-----END PUBLIC KEY-----", "");
        pubKey = pubKey.replace("\n", "");
        byte[] byteKey = Base64.decodeBase64(pubKey);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(byteKey);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return keyFactory.generatePublic(x509EncodedKeySpec);
        } catch (Exception e) {
            log.error("获取公钥出错", e);
            throw new AppException("获取公钥出错");
        }
    }

    /**
     * 获取RSA私钥(PKCS8)
     *
     * @param priKey 私钥字符串
     * @return 私钥
     */
    private static PrivateKey getPrivateKey(String priKey) {
        if (StringUtils.isBlank(priKey)) {
            return null;
        }
        priKey = priKey.replace("-----BEGIN PRIVATE KEY-----", "");
        priKey = priKey.replace("-----END PRIVATE KEY-----", "");
        priKey = priKey.replace("\n", "");
        byte[] byteKey = Base64.decodeBase64(priKey);
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(byteKey);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (Exception e) {
            log.error("获取私钥出错", e);
            throw new AppException("获取私钥出错");
        }
    }
    /**
     * 根据 baseSign 对参数进行签名
     * @param baseSign 基本签名参数
     * @param privateKey 私钥
     * @return 签名结果
     */
    public static String sign(BaseSign baseSign, String privateKey) {
        return sign(objectToTreeMap(baseSign), privateKey);
    }

    /**
     * 对参数进行签名
     *
     * @param params     有序的TreeMap参数
     * @param privateKey 私钥
     * @return 签名结果
     */
    public static String sign(TreeMap<String, Object> params, String privateKey) {
        return sign(Objects.requireNonNull(getSignContent(params)), privateKey);
    }

    /**
     * 私钥签名
     *
     * @param source     源数据:似 sign=xxx&name=yyy
     * @param privateKey 私钥
     * @return 签名
     */
    public static String sign(String source, String privateKey) {
        byte[] sourceData = source.getBytes();
        String result = null;
        try {
            PrivateKey priKey = getPrivateKey(privateKey);
            Signature sign = Signature.getInstance(SIGNATURE_ALGORITHM);
            sign.initSign(priKey);
            sign.update(sourceData);
            byte[] resultData = sign.sign();
            result = Base64.encodeBase64String(resultData);
        } catch (Exception e) {
            log.error("生成签名时发生异常", e);
        }

        return result;
    }

    /**
     *  将map转换为请求参数
     * @param baseSign 基本签名参数
     * @return 请求参数 类似 sign=xxx&name=yyyy
     */
    public static String getSignContent(BaseSign baseSign) {
        return getSignContent(objectToTreeMap(baseSign));
    }

    /**
     * 将map参数转化为请求参数
     * @param params treeMap参数
     * @return  请求参数 类似 sign=xxx&name=yyyy
     */
    public static String getSignContent(TreeMap<String, Object> params) {
        if (MapUtils.isEmpty(params)) {
            return null;
        }
        params.remove(SIGN_KEY);
        StringBuilder sb = new StringBuilder();
        params.forEach((k,v) -> sb.append(k).append("=").append(v).append("&"));
        // 删除最后一个&
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    /**
     * 公钥验签
     *
     * @param sign      签名
     * @param source    源数据
     * @param publicKey 公钥
     * @return 通过/不通过
     */
    public static boolean validSign(String sign, String source, String publicKey) {
        byte[] sourceData = source.getBytes();
        boolean result = false;
        try {
            PublicKey key = getPublicKey(publicKey);
            Signature verify = Signature.getInstance(SIGNATURE_ALGORITHM);
            verify.initVerify(key);
            verify.update(sourceData);
            byte[] decoded = Base64.decodeBase64(sign);
            result = verify.verify(decoded);
        } catch (Exception e) {
            log.error("验证签名时发生异常", e);
        }
        return result;
    }
    /**
     * object 转化为 treeMap
     * @param o 参数
     * @return map
     */
    public static TreeMap<String, Object> objectToTreeMap(Object o) {
        if (Objects.isNull(o)) {
            return null;
        }
        return new TreeMap<>(objectToMap(o));
    }

    /**
     * object 转化为 map
     * @param o 参数
     * @return map
     */
    public static Map<String, Object> objectToMap(Object o) {
        if (Objects.isNull(o)) {
          return null;
        }
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        Map map = null;
        try {
            String json = objectMapper.writeValueAsString(o);
            map = objectMapper.readValue(json, Map.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 通过参数验证签名是否正确
     * @param param 加密参数
     * @param publicKey 公钥
     */
    public static void validSignByParam(BaseSign param, String publicKey) {
        if (!validSign(param.getSign(), getSignContent(param), publicKey)) {
            throw new AppException("签名验证失败");
        }
    }

    public static void main(String[] args) {
        // 客户端加签
        OrderSaveDTO dto = OrderSaveDTO.builder()
                .storeId("006007")
                .outOrderNumber("MY202107281745001")
                .reserveName("张杰")
                .reservePhone("15298541236")
                .orderPackage("下单套餐")
                .unitPrice(new BigDecimal("1000"))
                .peopleNumber(10)
                .orderAmount(new BigDecimal("10000"))
                .orderNickName("zj")
                .orderTime(LocalDateTime.now())
                .build();
        String sign = sign(dto, PRIVATE_KEY);
        dto.setSign(sign);
        log.info("请求参数:{}", dto);
        // 服务端验签
        log.info("接受参数:{}", dto);
        log.info("验签成功:{}", validSign(dto.getSign(), getSignContent(dto), PUBLIC_KEY));
        log.info("验签加密参数:{}", getSignContent(dto));
    }
}
1.2 基本签名参数

直接用你的实体继承BaseSign 。

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotBlank;

/**
 * 基本签名参数
 *
 * @author Zhang Jie
 * @version 1.0.0
 * @since 2021/7/21 15:16
 */
@Data
public class BaseSign {
	@NotBlank(message = "sign不能为空")
	@ApiModelProperty("加密字符串")
	private String sign;
}

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "OrderSaveDTO",description = "保存的订单信息")
public class OrderSaveDTO extends BaseSign {

	@NotBlank(message = "门店ID不能为空")
	@ApiModelProperty("门店ID")
	private String storeId;

	@NotBlank(message = "订单号不能为空")
	@ApiModelProperty("美域甄品订单号")
	private String outOrderNumber;
}
// Controller调用
	@PostMapping("/save")
	@ApiOperation("同步保存订单")
	public AppResult save(@Valid  @RequestBody OrderSaveDTO param) {
		ordersService.save(param);
		return AppResult.ok();
	}
1.3 输出结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值