实现RSA算法及其依赖算法,UTF8<->Unicode;Base64;GCD;LCD;扩展欧几里得算法;快速积;快速幂;快速幂模;费马小定理;二次探测定理;埃氏筛法;米勒拉宾算法;RSA算法。
nodejs v12.14.0运行
"use strict";
// unicode <-> utf8
class UTF8 {
static native2utf8(codes) {
let r = [];
for (let i=0; i<codes.length; i++) {
let code = codes[i];
if (code <= 0x7f) {
r.push(code);
}
else if (code <= 0x7ff) {
r.push( (code >> 6) | 0xc0);
r.push( (code & 0x3f) | 0x80);
}
else if (code <= 0xffff) {
r.push( (code >> 12) | 0xe0);
r.push( ((code >> 6) & 0x3f) | 0x80);
r.push( (code & 0x3f) | 0x80);
}
else if (code <= 0x10FFFF) {
r.push( (code >> 18) | 0xf0);
r.push( (code >> 12) | 0xe0);
r.push( ((code >> 6) & 0x3f) | 0x80);
r.push( (code & 0x3f) | 0x80);
}
else
throw "Unsupport code : " + code;
}
return r;
}
static utf82native(bytes) {
let r = [];
let i = 0;
while (i<bytes.length) {
if ((bytes[i] & 0xf0) == 0xf0) {
r.push( (((bytes[i] & 0x7) << 18) | ((bytes[i+1] & 0x3f) << 12) | ((bytes[i+2] & 0x3f) << 6) | (bytes[i+3] & 0x3f) ));
i += 4;
}
else if ((bytes[i] & 0xe0) == 0xe0) {
r.push( ((bytes[i] & 0x0f) << 12) | ((bytes[i+1] & 0x3f) << 6) | (bytes[i+2] & 0x3f) );
i += 3;
}
else if ((bytes[i] & 0xc0) == 0xc0) {
r.push( ((bytes[i] & 0x3f) << 6) | (bytes[i+1] & 0x3f) );
i += 2;
}
else if ((bytes[i] & 0x7f) == bytes[i]) {
r.push( bytes[i] & 0x7f );
i += 1;
}
else
throw "error :" + bytes[i];
}
return r;
}
}
// Base64 encode & decode
class Base64 {
static TABLE = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'];
static encode(_arr) {
let arr = UTF8.native2utf8(_arr);
var div = Math.floor(arr.length / 3);
var mod = arr.length % 3;
var result = [];
var t = new Array();
for (var i=0; i<div; i++) {
t[0] = arr[i * 3];
t[1] = arr[i * 3 + 1];
t[2] = arr[i * 3 + 2];
result.push( this.TABLE[ t[0] >> 2 ] );
result.push( this.TABLE[ (t[0] << 6 & 0xff) >> 2 | (t[1] >> 4) ] );
result.push( this.TABLE[ (t[1] << 4 & 0xff) >> 2 | (t[2] >> 6) ] );
result.push( this.TABLE[ (t[2] << 2 & 0xff) >> 2 ] );
}
if (mod == 1) {
t[0] = arr[i * 3];
result.push( this.TABLE[ t[0] >> 2 ] );
result.push( this.TABLE[ (t[0] << 6 & 0xff) >> 2 ] );
result.push( '=' );
result.push( '=' );
}
else if (mod == 2) {
t[0] = arr[i * 3];
t[1] = arr[i * 3 + 1];
result.push( this.TABLE[ t[0] >> 2 ] );
result.push( this.TABLE[ (t[0] << 6 & 0xff) >> 2 | (t[1] >> 4) ] );
result.push( this.TABLE[ (t[1] << 4 & 0xff) >> 2 ] );
result.push( '=' );
}
return result.join("");
}
static decode(str) {
var result = [];
for (var i=0; i<Math.floor(str.length / 4); i++) {
var t = new Array();
t[0] = this.TABLE.indexOf( str.charAt(i * 4) );
t[1] = this.TABLE.indexOf( str.charAt(i * 4 + 1) );
t[2] = this.TABLE.indexOf( str.charAt(i * 4 + 2) );
t[3] = this.TABLE.indexOf( str.charAt(i * 4 + 3) );
result.push( (t[0] << 2 & 0xff) | t[1] >> 4 );
if (t[1] != -1 && t[2] != -1)
result.push( (t[1] << 4 & 0xff) | t[2] >> 2 );
if (t[2] != -1 && t[3] != -1)
result.push( (t[2] << 6 & 0xff) | t[3] ) ;
}
return UTF8.utf82native(result);
}
}
// 欧几里得算法 Greatest Common Divisor
function Euclidean(a, b) {
if (a < b) {
a = a + b;
b = a - b;
a = a - b;
}
let c = a % b;
while (c > 0) {
a = b;
b = c;
c = a % b;
}
return b;
}
// 扩展欧几里得算法
function ExtendedEuclidean(a, b) {
if (b == 0) {
return {a: a, b: b, x: 1, y: 0, r: a };
}
else {
let f = ExtendedEuclidean(b, a % b);
let y = f.x - parseInt(a/b) * f.y;
let x = f.y;
return {a: a, b: b, x: x, y: y, r: f.r };
}
}
// Least Common Multiple
function lcm(a , b) {
return a * b / Euclidean(a,b);
}
// 快速积算法
function mul(a, b) {
let r = 0, i = 1;
while (b > 0) {
let t = i;
if (b & 1 == 1) {
t *= a;
r += t;
}
i <<= 1;
b >>= 1;
}
return r;
}
// 快速幂算法
function pow(a, b) {
let binaryBitsLength = 0, t = b;
while (t != 0) {
t = Math.floor(t / 2);
binaryBitsLength ++;
}
let r = 1;
for (let i=1; i<=binaryBitsLength; i++) {
r = r * r;
if ( (b & (1 << binaryBitsLength-i)) != 0 )
r = r * a;
}
return r;
}
/*
蒙哥马利幂模运算
a ^ b % n => a ^ (b1+b2+bn) %n => (a^b1 * a^b2 * a^bn) % n => (a^b1 % n) * (a^b2 %n) * (a^bn %n)
*/
function Montgomery(a, b, n) { // Math.pow(a,b) % n
let binaryLength = 0, t = b;
while (t != 0) {
t = Math.floor(t / 2);
binaryLength ++;
}
let r = 1;
for (let i=1; i<=binaryLength; i++) {
r = r * r % n;
if ( (b & (1 << binaryLength-i)) != 0 )
r = r * a % n;
}
return r;
}
// 费马小定理
function Fermat(a, p) {
return Montgomery(a, p-1, p) == 1;
}
/*
二次探测定理
a^(p-1) = a^(m • 2^q) ≡ 1 (mod p) => a^(p-1) = (a^m)^(2^q) ≡ 1 (mod p) => (a^m)^2 = [(a^m)^2]^2 = [((a^m)^2)^2]^2 = X^2 ≡ 1 (mod p)
*/
function twice(a, p) {
let q = 0, m = p - 1;
while (m & 1 == 0) { // 2^q * m
q ++ ;
m >>= 1;
}
a = Montgomery(a, m, p);
for (let i=q; i>=1; i--) {
let r = a*a % p ;
if (r == 1 && a != 1 && a != p-1)
return false;
a = r;
}
return true;
}
// 埃氏筛法
function Eratosthenes(n) {
let bits = new Array(n);
bits.fill(1);
bits[1] = 0;
let i = 2;
while (i<n/2) {
if (bits[i] == 1)
for (let j=i+1; j<n;j++)
if (j % i == 0)
bits[j] = 0;
i++;
}
let out = [];
for (let i=1; i<n; i++) {
if (bits[i] == 1)
out.push(i);
}
return out;
}
/*
米勒-拉宾算法
*/
function MillerRabin(n) {
const basePrime = [2,3,5,7,11,13,17,19];
const carmichael = [561, 1105, 1729, 2465,2821,6601, 8911, 10585, 15841,29341,41041, 46657, 52633,62745, 63973, 75361 ];
let bits = new Array(n+1);
bits.fill(0);
basePrime.map(i=>{ bits[i] = 1 });
for (let i = 21; i<=n; i+=2 ) {
if (i & 1 == 0) // 是偶数
continue ;
if (carmichael.includes(i) )
continue ;
let b = true;
for (let j of basePrime) {
if (j >= i)
break ;
b = Fermat(j, i);
if (b == false) {
break;
}
b = twice(j, i);
if (b == false)
break ;
}
bits[i] = b ? 1 : 0;
}
let out = [];
for (let i=1; i<n; i++) {
if (bits[i] == 1)
out.push(i);
}
return out;
}
// RSA算法
class RSA {
constructor() {
let primes = Eratosthenes(Math.pow(2, 8));
let p = primes[ 2+parseInt(Math.random() * (primes.length-2))];
let q = primes[ 2+parseInt(Math.random() * (primes.length-2))];
while (p == q)
q = primes[ 2+parseInt(Math.random() * (primes.length-2))];
this.n = p * q;
let o = (p-1) * (q-1);
primes = MillerRabin(o);
this.key1 = primes[ 2+parseInt(Math.random() * (primes.length-2))];
console.debug("p=%d,q=%d,n=%d,φ=%d",p,q,this.n,o);
let r = ExtendedEuclidean(this.key1, o);
r.x2 = r.x;
while (r.x2 < 0) {
r.x2 += o;
}
console.debug("%dx+%dy=1 (%dx' mod %d=1) : x=%d, x'=%d, y=%d",r.a, r.b, r.a, o, r.x, r.x2, r.y);
this.key2 = r.x2;
console.debug("Key-1: ", this.key1, this.n);
console.debug("Key-2: ", this.key2, this.n);
if (this.key1 == this.key2) {
throw "key same";
}
}
encryption(s) {
let secret = [];
for (let i=0; i<s.length; i++) {
let code = s.charCodeAt(i);
secret.push( Montgomery(code, this.key1,this.n) );
}
return secret ;
}
decryption(secret) {
let plain = "";
for (let i=0; i<secret.length; i++) {
let code = secret[i];
plain += String.fromCharCode(Montgomery(code, this.key2, this.n));
}
return plain;
}
}
执行结果:
RSA Key1、Key2,Test