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

2018-08-27  本文已影响104人  djyuning

目前的项目需要用到加密算法,内部协商实现了一种简单的版本。这里使用了 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('');
}

注意的内容:

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

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'));

执行效果如下:


执行效果
上一篇下一篇

猜你喜欢

热点阅读