美文网首页
异或运算配合 RSA 实现加密和解密

异或运算配合 RSA 实现加密和解密

作者: djyuning | 来源:发表于2018-08-27 18:19 被阅读104次

    目前的项目需要用到加密算法,内部协商实现了一种简单的版本。这里使用了 2 种加密方式,一种是基于 Unicode 编码加密,一种是 RSA 加密。

    异或,英文为 exclusive OR,缩写成 xor,如:a = 1; b = 2; a^b=3

    RSA加密算法是一种非对称加密算法。在公开密钥加密电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

    我们实现的加密原理:系统生成 RSA 公钥和私钥,公钥使用 Unicode 编码加密处理后写入 JS 文件,用户提交敏感数据时,使用自定义解码规则解出 RSA 公钥,使用 RSA 加密数据传入后台,后台使用 RSA 私钥解析数据。

    首先是自定义使用的公钥和私钥,我测试的时候是使用 在线工具 生成的。
    它生成的字符是这样的:

    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbj+vUIk6Wruqa2HVZij9lr2LC
    TTLgsAvSa3+rTlBSfzkkQwtgENqe7ZcgZ6VPofasLTR3W6IAxqvh1jm8a/2f5IPL
    VEgq4cuj3Uu2TTOs054vcGztEIdrGzFYqEcS4PJVlCJtMqrsR+AOhGQQTMnwPiMJ
    LsC6sd3us9KKMn3sKQIDAQAB
    

    我们使用 NodeRSA 这个包进行 RSA 加密处理,所以,上面的字符串要适当修改一下:

    const PublickKey = '-----BEGIN PUBLIC KEY-----\n'+
                       'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDbj+vUIk6Wruqa2HVZij9lr2LC\n'+
                       'TTLgsAvSa3+rTlBSfzkkQwtgENqe7ZcgZ6VPofasLTR3W6IAxqvh1jm8a/2f5IPL\n'+
                       'VEgq4cuj3Uu2TTOs054vcGztEIdrGzFYqEcS4PJVlCJtMqrsR+AOhGQQTMnwPiMJ\n'+
                       'LsC6sd3us9KKMn3sKQIDAQAB\n'+
                       '-----END PUBLIC KEY-----';
    

    使用异或运算和 Unicode 编码实现加密解密:

    // 加密处理
    function encode(str, split) {
      let res = [];
      for (let i = 0; i < str.length; i++) {
        let a = str.charCodeAt(i); // 获取当前字符的 Unicode 编码数字
        let b = 'abcdefghijklmnopqrstuvwxyz'.charCodeAt(Math.ceil(Math.random() * 25)); // 使用随机字母作为异或运算混淆
        res.push([a ^ b, b].join(','));
      }
      return res.join(split);
    }
    
    // 解密处理
    function decode(str, split) {
      let res = [];
      str = str.split(split); // 解析出所有的字符
      str.forEach(s => {
        s = s.split(',');
        res.push(String.fromCharCode(s[0] ^ s[1])); // 使用异或运算还原原始字符
      });
      return res.join('');
    }
    

    注意的内容:

    • 加密和解密中使用的 split 设计具有迷惑性,如:我们设置为 ,0,加密后的密文则为:93,112,091,118,088,117,069,104,093,112,042,104,046,107,037,98的格式,看上去是一堆数字,解析的初始化实际为 93,112 | 91,118 | 88,117 | 69,104 | 93,112 | 42,104 | 46,107 | 37,98
    • 这里的加密和解密是忽略内容格式的,也就是说,传入和传出的值都是字符串,所以,在调用的时候,需要先把数据处理成字符串在进行加密;

    为了尽量保证公钥的不直接暴露在前台,我们需要使用上面的加密算法先把生成的公钥进行加密:

    let EndeoRSAPublicKey = encode(PublickKey, ',0');
    // 加密后的值:95,98,091,102,078,115,074,119,086,107,072,117,081,···略···,071,122
    

    此时 PublickKey 已经被加密处理了,而其真实的值已经不需要了,我们不应该把 encode 函数和 PublickKey 的真实值暴露在前台 js 中,而应该仅保留 decodeEndeoRSAPublicKey 的值

    在提交数据时,我们需要先把 EndeoRSAPublicKey 解析为真实的公钥,然后再使用 RSA 进行加密。

    // 引入 RSA 加密扩展
    import NodeRSA from 'node-rsa'
    import axios from 'axios'
    
    // 原始数据
    let data = {
      username: 'user',
      password: '123321',
    };
    
    let key = new NodeRSA(decode(EndeoRSAPublicKey, ',0'));
    axios.post('./test.do', key.encrypt(data,'base64')).then(res => {
      //
    }, err => {
      throw err;
    });
    

    后端需要使用对应的私钥进行数据解密。

    提供一个 NodeJs 版本的测试,将该文件存储为 rsa.js,然后终端中使用 node rsa.js 命令执行该文件的内容:

    const NodeRSA = require('node-rsa');
    
    // 后端提供的公钥
    const PublickKey = '-----BEGIN PUBLIC KEY-----\n' +
      'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCvcZwLmfIk9XOHUksUwsgB1mT\n' +
      'okM+aoRYR3E9NRcZ/Y+YjpUlnisk7LO26Ps4P3O1r1BW5ZBpWtQmzFjbNPB7PWXu\n' +
      'tavQycWTSM70RX2eardZabCDkCs8FztzTb2OH2i+1V6CWLO0hkBVWCSAPV5kfW12\n' +
      'L/vE+4a/8LKzuVDEmQIDAQAB\n' +
      '-----END PUBLIC KEY-----';
    
    const PrivateKey = '-----BEGIN PRIVATE KEY-----\n' +
      'MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMK9xnAuZ8iT1c4d\n' +
      'SSxTCyAHWZOiQz5qhFhHcT01Fxn9j5iOlSWeKyTss7bo+zg/c7WvUFblkGla1CbM\n' +
      'WNs08Hs9Ze61q9DJxZNIzvRFfZ5qt1lpsIOQKzwXO3NNvY4faL7VXoJYs7SGQFVY\n' +
      'JIA9XmR9bXYv+8T7hr/wsrO5UMSZAgMBAAECgYEAlXWE6PAUovIjM49ya1xIu4oo\n' +
      'i5ALP8oMTJx4IluuoTnjjVhQy5A62Jn5y7W/qQm5yoUEicyiKtmU3ToUMBjPPs1j\n' +
      'HJ2pjpJ1Qz4vWy1XPq6h8ozFSr6nrOw+9bk8Y5+TyGDFc6FDNjqt2Bg89xXSzwdx\n' +
      'Gp1ZyRjo1l6Qb2fPUMkCQQD7LoqLxQ+9Ssau7mRX+2iLVN8zg22KoLUBi3ylvSS3\n' +
      '+Vcbf69pGmfmpU2ii2RjmpVgnkYDpl4C9d9xlqCMaEmfAkEAxnoTd9qUE44fAW2x\n' +
      'FAuhnHuf3xpZLMpdSe2j5bVIrXtv9+9Fj86Z0vW+j24tAkytDRy0GFGqBMgNdjBA\n' +
      'IEg2xwJAPaQPRfunQCnglj9Uiq7c2gyK9eZT9Ig5w1ZK0ZWYNDnRYaM1FdLwGo8I\n' +
      'fVI94Z+m9t4AipbCTXGvUv3HCo3xOwJBAJmFsetiemmJ5DfRpkhQGukUwvvqwJGh\n' +
      '0nktxToYeKggM+K/BLqQ33FLvuPpIA2IS885paCuAmoCaE9EUUXnNd8CQQDn/66B\n' +
      'LGHLJjBcav7PKQkMSubt/BQnNfjZMnAMMbR7/V/uFFHuqrN/3HuIGuOt5HVaSUoR\n' +
      '2G8ESeKbcCrFQ86n\n' +
      '-----END PRIVATE KEY-----';
    
    // 加密处理
    function encode(str, split) {
      let res = [];
      for (let i = 0; i < str.length; i++) {
        let a = str.charCodeAt(i); // 获取当前字符的 Unicode 编码数字
        let b = 'abcdefghijklmnopqrstuvwxyz'.charCodeAt(Math.ceil(Math.random() * 25)); // 使用随机字母作为异或运算混淆
        res.push([a ^ b, b].join(','));
      }
      return res.join(split);
    }
    
    // 解密处理
    function decode(str, split) {
      let res = [];
      str = str.split(split); // 解析出所有的字符
      str.forEach(s => {
        s = s.split(',');
        res.push(String.fromCharCode(s[0] ^ s[1])); // 使用异或运算还原原始字符
      });
      return res.join('');
    }
    
    // 加密处理
    let test = 'aX4q/NlmeG5E5TndKCJEOgJJnmByo4Sd5bEGl6CaP7KypyfKYkBrA2Zlh4mPtDZ9F0f7J48LRW88eZWATNuXEA3FTfNgnwpRfwLWttunaQAZZjTMTE4BsszYOyKIGzDeKD4S9xzasW+/46jI3Dw7+Oc7u6/FHWysEfPyyAiuGGsEEouuHJ6h0NHNM5lRYWw+cGG2wH41iNpm7VoAqFQ6l+hWD9ZfcA3OXXWxE4xRhkhZ3W4XYjNvPR2+lC852HQiOk0wWdHnH9hjKOxgKSrahTvk8+msae38gn4rTa3hbxudezcsRqSG+caC7+/QUxNxoqlbcHr/1gCmX4cFgxQIxS4IigSFOF/t56O4Hytef4BihDdKemNMWMgqzzjs+rzZYW66ofEmzvpM4zIsp1DkAG1BuS5d2jzdHVgXPI0YAg4lCCILr9vFrGb1tKexGWSECRMP4mj4w0TsLhlLjEBVCc119uYSqLOWjDhhIrpNaEiNDbyV1nnrJ0XCl4/n83d/';
    let split = ',0';
    
    let output = encode(PublickKey, split);
    console.log('加密后的公钥:', output);
    let realPublicKey = decode(output, split);
    console.log('解密出的原始公钥:', realPublicKey);
    let deKey = new NodeRSA(PrivateKey);
    console.log('测试数据:', test);
    console.log('私钥解析出的测试数据:', deKey.decrypt(test, 'utf-8'));
    

    执行效果如下:


    执行效果

    相关文章

      网友评论

          本文标题:异或运算配合 RSA 实现加密和解密

          本文链接:https://www.haomeiwen.com/subject/hfutwftx.html