前端RSA ,ASE 加解密踩坑记录

Rsa 加解密

使用的插件:JSEncrypt.js
加密方式:

 let encryptor = new JSEncrypt();
      encryptor.setPublicKey(pubkey); // 加密公钥
      return encryptor.encrypt(data); // data: 要加密的数据

解密方式:

  let decryptor = new JSEncrypt();
     decryptor.setPrivateKey(PrivateKey); // 私钥
     return decryptor.decrypt(data); // 要解密的字符

上面使用到的公私钥生成方式:

// 生成公私钥
    async generateKey() {
      //获取密钥对
      const getRsaKeys = text => {
        return new Promise((resolve, reject) => {
          window.crypto.subtle
            .generateKey(
              {
                name: "RSA-OAEP",
                modulusLength: 2048, //  1024, 2048, 4096
                publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
                hash: { name: "SHA-512" } //可以是“SHA-1”、“SHA-256”、“SHA-384”或“SHA-512”
              },
              true, //密钥是否可提取(即可用于exportKey)
              ["encrypt", "decrypt"] //必须是[“encrypt”,“decrypt”]或[“wrapKey”,“unwrapKey”]
            )
            .then(function(key) {
              window.crypto.subtle
                .exportKey("pkcs8", key.privateKey)
                .then(function(keydata1) {
                  window.crypto.subtle
                    .exportKey("spki", key.publicKey)
                    .then(function(keydata2) {
                      var privateKey = RSA2text(keydata1, 1);
                      var publicKey = RSA2text(keydata2);
                      resolve({ privateKey, publicKey });
                    })
                    .catch(function(err) {
                      console.error(err);
                    });
                })
                .catch(function(err) {
                  console.error(err);
                });
            })
            .catch(function(err) {
              console.error(err);
            });
        });
      };
      const RSA2text = (buffer, isPrivate = 0) => {
        var binary = "";
        var bytes = new Uint8Array(buffer);
        var len = bytes.byteLength;
        for (var i = 0; i < len; i++) {
          binary += String.fromCharCode(bytes[i]);
        }
        var base64 = window.btoa(binary);
        var text ="";
          // "-----BEGIN " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----\n";  // 一般生成的密钥有前后缀,我的项目没有使用到,所以注释
        text += base64
          .replace(/[^\x00-\xff]/g, "$&\x01")
          .replace(/.{64}\x01?/g, "$&\n");
        // text +=
        //   "\n-----END " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----";// 一般生成的密钥有前后缀,我的项目没有使用到,所以注释
        return text;
      };
      return getRsaKeys();
    },

注意代码中注释部分!

遇到的问题:

rsa加密有长度限制

rsa算法本身要求加密内容也就是明文长度m必须0<m<密钥长度n。如果小于这个长度就需要进行padding,因为如果没有padding,就无法确定解密后内容的真实长度,字符串之类的内容问题还不大,以0作为结束符,但对二进制数据就很难,因为不确定后面的0是内容还是内容结束符。而只要用到padding,那么就要占用实际的明文长度,于是实际明文长度需要减去padding字节长度。我们一般使用的padding标准有NoPPadding、OAEPPadding、PKCS1Padding等,其中PKCS#1建议的padding就占用了11个字节。
这样,对于1024长度的密钥。128字节(1024bits)-减去11字节正好是117字节,但对于RSA加密来讲,padding也是参与加密的,所以,依然按照1024bits去理解,但实际的明文只有117字节了。
所以如果要对任意长度的数据进行加密,就需要将数据分段后进行逐一加密,并将结果进行拼接。同样,解码也需要分段解码,并将结果进行拼接。

遵循这一规则,我在项目中使用字符切割加密,但是和后台交付无法正常解密,通过一起排查,原来是
JSEncrypt.js 加密是没有考虑分段加密的,每次加密直接帮我们转了一次base64码 ,而我们分段加密需要把每一段先加密后拼接,再转base64码 才可以正常加解密使用,扩展JSEncrypt.js以达到支持分段加密:

JSEncrypt.prototype.encryptLong = function (str) {
  try {
		var inputLen = str.length,
		offset = 0,
		i = 0,
		cache =[];
		while(inputLen - offset > 0){
			if(inputLen - offset > 117){
			    cache.push(this.getKey().encrypt(str.slice(offset,offset+117)));
			}else{
			    cache.push(this.getKey().encrypt(str.slice(offset,inputLen)));
			}
		   offset = ++i * 117;
		}
    return hex2b64(cache.join(""));
  }
  catch (err) {
	  console.log(err);
    return false;
  }
};

ASE加解密:

使用的插件:crypto-js
加密方式:

 // 加密
 /**
 *@params:
 *text: 加密内容
 *iv: 偏移量
 *key: 密钥
 */
    encrypt(text,iv,key) {
      return CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(key), {
        iv: CryptoJS.enc.Utf8.parse(iv),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
      }).toString();
    }

解密方式:


    // 解密
 /**
 *@params:
 *text: 解密内容
 *iv: 偏移量  与加密时保持一致
 *key: 密钥  与加密时保持一致
 */

    decrypt(text,iv,key) {
      let decrypted = CryptoJS.AES.decrypt(
        text,
        CryptoJS.enc.Utf8.parse(key),
        {
          iv: CryptoJS.enc.Utf8.parse(iv),
          mode: CryptoJS.mode.CBC,
          padding: CryptoJS.pad.Pkcs7
        }
      );
      return decrypted.toString(CryptoJS.enc.Utf8);
    }

遇到的问题:

在这里插入图片描述
解决方案参考:
以下都是不同的错误报出上面问题的解决方案
1 https://blog.csdn.net/weixin_42124196/article/details/88555847
2 https://www.cnblogs.com/wangyang0210/p/13594019.html
3 https://segmentfault.com/a/1190000017540855

而我这里因为是与后台交互,遇到的问题与第二个有点类似,是key的类型没做处理、和mode设置不一致导致的,具体解决方案代码如下:

  // 解密
    decrypt(text, key) {
      key = CryptoJS.enc.Utf8.parse(key.toString());  // 1 注意把key toString()处理
      key = CryptoJS.enc.Base64.stringify(key);  // 2
      key = CryptoJS.enc.Base64.parse(key);  // 3
      let decrypted = CryptoJS.AES.decrypt(
        text,
        key,
        {
          mode: CryptoJS.mode.ECB,
          padding: CryptoJS.pad.Pkcs7
        }
      );
      return decrypted.toString(CryptoJS.enc.Utf8);
    },

注意代码中 注释的1、2、3,对Key处理的部分

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

caperxi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值