Vue前端使用jsrsasign工具RSA签名与后台Java拦截器,JAVA实现RSA签名、验签功能

本文介绍RSA算法原理及其在数字签名中的应用,包括前后端如何实现签名与验签过程,确保数据传输的安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现在网站都是前后台分离模式,在客户端和服务端的通信过程中,会遇到很多的安全问题,无法确认收到的信息是否是真实有效的,而不是中途被人掉包的。这个时候数字签名就可以站出来,它的作用就是用来征明消息在通信过程中未被掉包,是真实有效的。

1977年,由三位数学家(Rivest、Shamir 和 Adleman )联合发表了RSA算法,算法名称的来源是三位科学家的首字母的和。
RSA加密是一种非对称加密。是由一对密钥来进行加解密的过程,分别称为公钥和私钥。可以在不直接传递密钥的情况下,完成解密。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。并且随着密钥长度的不断增加,以当下的计算机运算水平,是不可能在有限的时间下,暴力破解出密钥,而攻破该算法的。这能够确保信息的安全性,避免了直接传递密钥所造成的被破解的风险。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。

加密、签名区别

加密和签名都是为了安全性考虑,但略有不同。常有人问加密和签名是用私钥还是公钥?其实都是对加密和签名的作用有所混淆。简单的说,加密是为了防止信息被泄露,而签名是为了防止信息被篡改。本文主要讲解签名过程。

RSA签名(私钥签名、公钥验签)的过程如下:

  • 用户注册生成一对密钥(公钥和私钥),私钥不公开,用户自己保留。公钥保存在服务端保管。
  • 用户自己的私钥对消息加签,形成签名,并将加签的消息和消息本身一起传递给服务器。
  • 服务器收到消息后,在获取用户的公钥进行验签,如果验签出来的内容与消息本身一致,证明消息是该用户的没有被篡改。
    业务流程图如下
    在这里插入图片描述

生成RSA公钥私钥

本文为了使用简单不重复造轮子使用hutool 工具包作为生成工具和验签工具
Hutool针对java.security.Signature做了简化包装,包装类为:Sign,用于生成签名和签名验证。链接
maven引入hutool工具包

		<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.22</version>
        </dependency>

后端生成公钥私钥代码

    public static void main(String[] args) {
        RSA rsa= SecureUtil.rsa();
        String priKey= rsa.getPrivateKeyBase64();
        System.out.println("public static String priKeyText=\""+priKey+"\";");
        String pubKey=  rsa.getPublicKeyBase64();
        System.out.println("public static String pubKeyText=\""+pubKey+"\";");
    }
    public static String priKeyText="MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAM2yllVX45houpxHKUkRUIm7XjobsQxF6R6Uo++PPRi8yEplO/jUWGdFKsl7KA75umWeaLcCmhmkZMDCtjYRGl7yD/H9+9wsvCUA5AONHqLPBWpeKUiEz5QZb3lM4p0ZqDHYYg+pnRecNVDi3gRSpGbEnfPGJXNmTsHf/g7jyVJzAgMBAAECgYAJBguYRNnEJtwA3RJPlUXeNO0GSY6zxiFa6RRj/vmkKyvyL4y49r/GD/+3hQKV3ZiyuodHeALAmzicSC5sIsXwTibDk+xeO2MV2MqYj5u+s1iNUfVhYaKKZUVxZaTuhVnwjYutgGPq4UWV2Lk6jfIoKQgfbJ4JtwJSgKImp+ZuQQJBAPfH8KYPjomc6x46384t97S6WunUyL2LnAEpWZIpTjporVdndMt7qrqYwq2a7wcMcBevUw4Py83GXIyEgBER9GECQQDUhUqQpgi9RcLYYZs5WP/z+2Vsyb03jmgg1iMEJ8vdKLsiIIiEyFuXCmyzQHnmHBg6ZaNp3P5R6wKrewu/HndTAkEA1U/HmO6jo0z4DNpaekGQkoIsaknJV4StFULn3bC5rAcZ07k5D0SzwlhooNRVGxhWF83UDrMCnXA7lK0tI4T1gQJBAKRcykSBBPVNhNpQ8voxaRNLdS/j/uF+0+3Bb6eBF9xd6g0E2FDJnHx+ZX9k+GINjHdtsCrp7g9xEMmLCpSvfV0CQCP3V8nvi8OTd8h+454XuvL45mBKuiN4PjVcfMjmlNQawSWx/FRgna4F7d44K/Zo8fTW/rgCNZp4fXLluIi8lEY=";
	public static String pubKeyText="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNspZVV+OYaLqcRylJEVCJu146G7EMRekelKPvjz0YvMhKZTv41FhnRSrJeygO+bplnmi3ApoZpGTAwrY2ERpe8g/x/fvcLLwlAOQDjR6izwVqXilIhM+UGW95TOKdGagx2GIPqZ0XnDVQ4t4EUqRmxJ3zxiVzZk7B3/4O48lScwIDAQAB";

主要流程在前端对请求内容使用私钥进行签名,把签名内容写入到请求头header在Java后台拦截件获取签名内容使用公钥进行验签操作,若是一致则认为请求参数没有被篡改,若是不一致则认为请求参数已经被篡改,拦截请求。

前端签名

引入依赖

//主要包
npm install jsencrypt
//辅助包提供了各种各样的加密算法。
npm install crypto-js

前端签名

#引入模块
import {JSEncrypt} from "jsencrypt";

/** 签名方法 */
function rsaSign(plainText){
  let priKey= "填入上方生成的priKey";
  //这里必须使用这个标准格式所有做了一个拼接
  var priK = "-----BEGIN PRIVATE KEY-----"+priKey+"-----END PRIVATE KEY-----";
  var rsa = new jsrsasign.RSAKey();
  rsa = jsrsasign.KEYUTIL.getKey(priK);
  //后端算法与前端需要一致
  let sig = new jsrsasign.KJUR.crypto.Signature({"alg": "SHA256withRSA"});
  // 初始化
  sig.init(rsa)
  console.log("data:"+plainText);
  // 传入待加密字符串
  sig.updateString(plainText)
  // 生成密文
  let sign = jsrsasign.hextob64(sig.sign());
  console.log("sign:"+sign);
  // 对加密后内容进行URI编码
   sign = encodeURIComponent(sign);
   console.log("sign encode:"+sign);
   return sign;
}

效验签名

//需要把对应的拦截器注册到WebMvcConfigurer
registry.addInterceptor(signInterceptor).addPathPatterns("/**");

/**
* 签名验签拦截器
*/
@Component
@Slf4j
public class SignInterceptor implements HandlerInterceptor {
    private final static String priKeyText="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALkYLvaYdloINgYnT5nrUMFd/3ghrUeV65CbueEwBkmwydPJOQkulumNCx2JsqZKLtOME1BCzzfVvAkZZzMmEXXUdweZee0NFiTrFKzCQaLVN7XhHZyb+6nFsAf31jwCm2LU3Y7yqni2dZPMNhL2W7UM6JtewyWKbqJwnrhpV1hlAgMBAAECgYBWKP67hY8aK9ZSGPyB7rshuArStgJ+XzhPkV3+iCd1KBlbP8EQGCLhxukUr+N8au3PRdY7t03UdObZ6XxTn/Xh88FGpvNtP8Xt2NXPmPdHnr8yS8ro3xoyjnFpnV8ILoVXB3wcH7sMyJlLbFOfNopd34UnOHq8i0G4UMufDfF/5QJBAOTxaw/4lU00ng/2Gqv/3IQ5/LlfoTCnyGd+wni+UXuwMhZm9EgwfjZFStNmm7To2MDtgaRLcllGbzNKkleEHScCQQDO+CTUiqFomWvyQfiITpvYwCeDHZkNxSKW/rIAJkcbIjnKoTyBjLIX4aO6tnrSGMYtfofWKPIE9Oov5PiEB22TAkAlyLIisKPzWfu8JqHAQTnjmK5c6atwnEA21HTD+KT6BNo/WD54q4go7MasnQKVmNgs/wWmV81HbphrnqMIToWBAkAW26UFrEHjUX5whQSz2SqxJ5e1jWFH+gu17W8vQQr9XixPyrrp++X3aT4x8tFhvsa0Y5MQ/fisv3fVzk38Fu3tAkEAqYWdIuBUtweHI5GU+eOy/B3/WJQYyhTQ7TIXJvzHlxy/YsCrN3FShkvbMda1johv6Js0rvTP8PFi8JfmcjlBkw==";
    private final static String pubKeyText="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5GC72mHZaCDYGJ0+Z61DBXf94Ia1HleuQm7nhMAZJsMnTyTkJLpbpjQsdibKmSi7TjBNQQs831bwJGWczJhF11HcHmXntDRYk6xSswkGi1Te14R2cm/upxbAH99Y8Apti1N2O8qp4tnWTzDYS9lu1DOibXsMlim6icJ64aVdYZQIDAQAB";
    private Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA,priKeyText,pubKeyText);
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果不是映射到方法直接通过
        if (handler instanceof HandlerMethod) {
            if (!HttpMethod.GET.name().equals(request.getMethod())) {
                String signStr = request.getHeader("sign");
                if (Objects.isNull(signStr)) {
                    AjaxResult ajaxResult = AjaxResult.error("签名错误:签名为null");
                    ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
                    return false;
                }
                signStr=URLUtil.decode(signStr);
                String body = HttpHelper.getBodyString(request);
                boolean verify= sign.verify(body.getBytes(), Base64.decode(signStr));
                if (!verify) {
                    AjaxResult ajaxResult = AjaxResult.error("签名错误:验签错误");
                    ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
                    return false;
                }
            }
            return true;
        } else {
            return true;
        }
    }
}
引用\[1\]和\[3\]提供了关于在Vue前端使用AES加密和解密的代码示例。在Vue前端使用AES加密和解密,你可以按照以下步骤进行操作: 1. 首先,确保你已经引入了CryptoJS库。你可以使用npm或者直接在HTML中引入。 2. 在你的Vue组件中,导入CryptoJS库,并定义密钥和偏移量。你可以参考引用\[3\]中的代码示例。 3. 创建一个加密函数和一个解密函数。你可以使用CryptoJS.AES.encrypt()方法进行加密,使用CryptoJS.AES.decrypt()方法进行解密。在加密和解密函数中,你需要传入要加密或解密的数据,以及密钥和偏移量。你可以参考引用\[3\]中的代码示例。 4. 在需要加密或解密的地方调用相应的函数即可。 需要注意的是,密钥和偏移量需要后台保持一致。在引用\[2\]中提供了一个Java后台的AES解密示例,你可以参考该示例来保持密钥和偏移量的一致性。 总结起来,你可以在Vue前端使用CryptoJS库来进行AES加密和解密。通过定义密钥和偏移量,并创建相应的加密和解密函数,你可以在需要的地方调用这些函数来实现加密和解密操作。 #### 引用[.reference_title] - *1* [关于javavue前端使用AES或者RSA加密后通过拦截器自动解密的实现+ RSA加密AES秘钥,AES加密data数据](https://blog.csdn.net/qq_25995697/article/details/118028562)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Vue前端密钥AES加密+Java后台AES解密](https://blog.csdn.net/weixin_43484014/article/details/121289917)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值