RSA公钥验签

1.业务场景,公司做理财业务,但是可能有第三方合作。与第三方合作获得更多客户流量。别人可以在第三方进行购买理财产品。那么怎么保证交易信息的安全性那,我们这里给出rsa加密实现原理。

2.工具类rsa:

公钥私钥的生成百度一下有在线生成的网站。

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 org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
/**
* 使用私钥将明文进行签名生成秘闻串与明文一起传输。对方收到数据后通过公钥对明文与明文进行验签。
* 若验签通过就说明第一数据没有被修改过;第二这些数据一定是持有私钥的人发送的,因为私钥自己持有,
* 这就起到了防止抵赖的作用。
*/
@Slf4j
public class RSAUtil {
static Logger LOG = LoggerFactory.getLogger(RSAUtil.class);
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; //签名算法
private static final String KEY_ALGORITHM = "RSA"; //加密算法RSA
/**
* 公钥验签
*
* @param text 原字符串
* @param sign 签名结果
* @param publicKey 公钥
* @return 验签结果
*/
public static boolean verify(String text, String sign, String publicKey) {
try {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
PublicKey key = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
signature.initVerify(key);
signature.update(text.getBytes());
return signature.verify(Base64.decodeBase64(sign));
} catch (Exception e) {
LOG.error("验签失败:text={},sign={}", text, sign, e);
}
return false;
}
/**
* 签名字符串
*
* @param text 需要签名的字符串
* @param privateKey 私钥(BASE64编码)
* @return 签名结果(BASE64编码)
*/
public static String sign(String text, String privateKey) {
byte[] keyBytes = Base64.decodeBase64(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
try {
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateK);
signature.update(text.getBytes());
byte[] result = signature.sign();
return Base64.encodeBase64String(result);
} catch (Exception e) {
LOG.error("签名失败,text={}", text, e);
}
return null;
}
private static final String publicKey = "aaaaaaaaa" ;
private static final String privateKey = "bbbbbbbbb";
public static void main(String[] args) {
String text = "hello world";
String sign = RSAUtil.sign(text, privateKey);
log.info(sign);
boolean verify = RSAUtil.verify(text, sign, publicKey);
log.info("result: {}",verify);
}
}

3.对数据进行加密之前,首先要保证数据的顺序一致性,顺序不一致可能会导致生成的密文不同。我们这里默认采用拼音排序。

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.happylaishop.admin.util.JsonUtil;
/**
* 签名明文,字典即拼音进行排序
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder(alphabetic = true)
public interface SignText {
default String toText(){
return JsonUtil.obj2String(this);
}
}

4.采用AOP进行校验。请求方法中前三个参数是string, string SignText的我们进行拦截校验。

authId表示认证对象id,双方约定好。

sign 表示密文签名

text 传输的数据

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import xxx.util.RSAUtil;
/**
* 验签aop
*/
@Component
@Aspect
public class SignAop {
@Autowired
private SignService signService;
@Before(value = "execution(* com.happylaishop.admin.controller.*.*(..)) && args(authId,sign,text,..)")
public void verify(String authId,String sign,SignText text){
/**根据认证id获取对应的公钥*/
String publicKey = signService.getPublicKey(authId);
//拿到公钥之后验签,若验签通过,执行后续业务逻辑,否则报异常
Assert.isTrue(RSAUtil.verify(text.toText(),sign,publicKey),"验签失败");
}
}
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* 签名服务
*/
@Service
public class SignService {
static Map<String,String> PUBLIC_KEYS = new HashMap<>();
static {
PUBLIC_KEYS.put("1000","aaaaaaaa");
}
/**
* 根据授权编号获取公钥
* @param authId
* @return
*/
public String getPublicKey(String authId){
return PUBLIC_KEYS.get(authId);
}
}

我们这里给出一个传输数据,订单对象;

import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
@Getter
@Setter
@ToString
public class OrderParam implements SignText{
private String chanId;
private String chanUserId;
private String productId;
private BigDecimal amount;
private String outerOrderId;
private String memo;
@JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
private Date createAt;
public static void main(String[] args) {
OrderParam order = new OrderParam();
order.setAmount(new BigDecimal("100"));
order.setChanId("1000");
order.setChanUserId("1000");
order.setProductId("1");
SignText signText = order;
String text = signText.toText();
System.out.println(text);
}
}


转载于:https://www.cnblogs.com/jpfss/p/10076602.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值