HTTPS完全通关(一)|RSA非对称加密算法JS实现

886 阅读2分钟

1. 前言

之前被面试官问了https,从CA问到密钥交换,从rsa问到ecc。怀疑是上一位面试的葛哪儿嗯卷,然后刚好我信安专业的就继续问了。毕业之后就没看过,不多说,js手撕一遍整套流程复习就完事儿了

2. RSA算法

实际上面试只需要知道四个知识点一般就够了

2.1 非对称加密算法在https中用来对 对称加密算法的密钥 进行加密

2.2 私钥加密 -〉 公钥解密 公钥加密 -〉 私钥解密

2.3 RSA是非对称加密算法

2.4 数论基础就是基于大数分解的困难度

3. 算法实现

关于对称加密非对称加密的定义以及RSA加密的整个流程,请参考阮一峰教程。此处将阮一峰教程分步解析为js代码

本文只分步编写容易理解的代码,暂不考虑代码性能

第一步,随机选择两个不相等的质数p和q

function isPrimeNumber(v) {
  for (let i = 2; i < v; i++) {
    if (v % i === 0) {
      return false;
    }
  }
  return true;
}

function getPrimeNumber(min, max) {
  let rst = [];
  for(let i = Math.max(2, min); i <= max; i++) {
    if(isPrimeNumber(i)) {
      rst.push(i);
    }
  }
  return rst;
}

let primeNumberList = getPrimeNumber(10, 100);
const pIndex = Math.floor(Math.random() * 1000) % primeNumberList.length;
const p = primeNumberList[pIndex];
primeNumberList.splice(pIndex, 1);
const qIndex = Math.floor(Math.random() * 1000) % primeNumberList.length;
const q = primeNumberList[qIndex]

第二步 计算p和q的乘积n

const n = p * q;

第三步 计算n的欧拉函数φ(n)

const olaN = (p-1) * (q-1);

第四步 随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质(辗转相除法)

function getGCD(a, b) {
  const big = a > b ? a : b;
  const small = a < b ? a : b;
  if (big%small===0) return small;
  return getGCD(big%small, small);
}

// range: [min, max)
function getRandom(min, max) {
  return Math.floor(Math.random() * (max-min)) + min;
}

primeNumberList = [];

for(let i = 1; i < olaN; i++) {
  if (getGCD(i, olaN) === 1) primeNumberList.push(i);
}

const e = primeNumberList[getRandom(2, primeNumberList.length)];

第五步 计算e对于φ(n)的模反元素d(扩展欧几里得算法计算乘法逆元)

function exGetGCD(a, b, receiveObj) {
  if (b === 0) { // 递归基取1, 0
    receiveObj.x = 1;
    receiveObj.y = 0;
    return a;
  }
  
  const greatestCommonDivisor = exGetGCD(b, a%b, receiveObj);
  const temp = receiveObj.y;
  receiveObj.y = receiveObj.x - Math.floor(a/b) * receiveObj.y;
  receiveObj.x = temp;
  return greatestCommonDivisor;
}

const receiveObj = {x: null, y: null};
const testVal = exGetGCD(e, olaN, receiveObj); // testVal = 1, just for teaching
while (receiveObj.x < 0) {
  receiveObj.x += olaN
}

const d = receiveObj.x;

第六步 将n和e封装成公钥,n和d封装成私钥

console.log(`public key: (${n}, ${e})`);
console.log(`private key: (${n}, ${d})`)
console.log(`p, q, olaN: ${p}, ${q}, ${olaN}`)
console.log(`n, e, d: ${n}, ${e}, ${d}`)

第七步 RSA算法的可靠性(证明过程略)

第八步 加密和解密

const BigNumber = require('big-number')

function rsaEncrypt(n, e, content) {
  return BigNumber(content).pow(e).mod(n);
}

function rsaDecrypt(n, d, content) {
  return BigNumber(content).pow(d).mod(n);
}

屏幕快照 2021-12-23 下午7.51.29.png

第九步 运行


const content  = '69';
console.log('content: ', content);
const encryptedContent =  rsaEncrypt(n, e, content)
console.log('encrypted content: ', encryptedContent.toString())
const decryptContent = rsaDecrypt(n, d, encryptedContent)
console.log('decrypted content: ', decryptContent.toString())

4. 结尾

其实什么库都不想引入的,下篇ECC写完之后把bignumber计算库撸一撸,造轮子舒服~造轮子快乐~ 随手点个👍吧