【问题标题】:Get the congruence of big numbers javascript RSA algorithm获取大数的一致性 javascript RSA 算法
【发布时间】:2019-09-28 13:39:34
【问题描述】:

在 RSA 非对称加密算法中,每个用户都有一个公钥 (n,e) 和一个私钥 (d) 来发送并接收来自其他用户的加密消息。

要加密消息,它会将字符更改为它们的 ascii 代码:

HELLO -> 72-69-76-76-79

要发送使用 RSA (c) 加密的消息,必须计算

c = m^e % n

使用公钥ne为消息中的每个字符m

要解密用户收到的消息,必须计算:

m = c^d % n (is the same to say m is congruent to c^d mod n)

对每个数字使用私钥d


一个小例子:

用户 Beto 获得了公钥:

n = 64523 
e = 127

和他的私钥:

d = 15583

如果某个用户想向 Beto 发送消息:

ABAC -> 65-66-65-67

加密用户必须计算的消息

65^127 % 64523 = 27725
66^127 % 64523 = 6407
65^127 % 64523 = 27725
67^127 % 64523 = 2523

加密后的代码是27725-6407-27725-2523

贝托要破译消息必须计算:

27725^15583 % 64523 = 65
6407^15583 % 64523 = 66
27725^15583 % 64523 = 65
2523^15583 % 64523 = 67

他得到了解密消息65-66-65-67 => ABAC


现在的问题:

我有这段代码来解决最后一部分,但我不能将它用于大数字(如示例中的那些):

function getCongruence(c, d, n) {
  return Math.pow(c,d) % n;
}

console.log(getCongruence(5,3,7)); // = 6 cuz 5^3=125 and 125 % 7 => 125 - 7*17 = 125 -119
console.log(getCongruence(19,11,17)); // = 8 cuz 19^11=116490258898219 % 17 = 8
console.log(getCongruence(27725,15583,64523)); // should be 65 but it shows NaN
.as-console-wrapper { max-height: 100% !important; top: 0; }

如果使用大数字,我怎样才能得到结果?

我可以使用其他算法来找到答案吗?

有我可以使用的库吗?

【问题讨论】:

  • javascript 确实有一个 bigInt 对象来处理大于 2^53-1 的数字。
  • @PeterN 我知道,这就是我提出问题的原因。
  • 即使该函数工作它仍然无法工作(哈哈),我的意思是,你不能将求幂和模约简分开,中间结果会变得太大,至少对于合理的消息和密钥大小。
  • 顺便说一下,加密单个 ASCII 字符是毫无用处的,这只会把它变成一个替换密码,你可以通过频率分析来破解它。
  • @harold 首先我是这么认为的,这就是我正在寻找另一种方法来获得它的原因,其次是放轻松,这只是教授算法的一个例子。

标签: javascript algorithm math bignum


【解决方案1】:

编辑

根据@harold 的建议,平方的迭代取幂是最快的方法,我将把我原来的幼稚递归方法留在下面进行比较。

编辑 2

添加了处理非常小的数字的 BigInt 的逆函数。 使用@harold 的建议在函数内部移动模减少以提高性能。

平方的迭代取幂:

const handleBigInverse = x => {
  const stringX = x.toString();

  if (stringX.length > 21) {
    const approximate = Number(stringX.slice(0, 21));
    const e = stringX.length - 21;

    const inverse = 1 / approximate;
    const inverseString = inverse.toString();

    const splitString = inverseString.split("e");
    splitString[1] = (Number(splitString[1]) - e).toString();

    return splitString.join("e");
  } else {
    const inverse = 1 / Number(x);
    return inverse.toString();
  }
};

const iterativeExpBySqWithMod = (x, n, mod) => {
  let bigX = BigInt(x);
  let bigN = BigInt(n);

  if (n < 0) {
    if (!mod || Math.abs(mod) >= 1) {
      return handleBigInverse(iterativeExpBySqWithMod(x, -n));
    } else {
      return (
        handleBigInverse(iterativeExpBySqWithMod(x, -n)) % mod
      ).toString();
    }
  }
  if (mod) {
    const bigMod = BigInt(mod);
    let result = BigInt(1);

    while (bigN > 0) {
      if (bigN % BigInt(2) == 1) {
        result = (result * bigX) % bigMod;
      }
      bigX = (bigX * bigX) % bigMod;
      bigN /= BigInt(2);
    }
    return result;
  } else {
    let result = BigInt(1);
    while (bigN > 0) {
      if (bigN % BigInt(2) == 1) {
        result *= bigX;
      }
      bigX *= bigX;
      bigN /= BigInt(2);
    }
    return result;
  }
};

// Big numbers output a bigInt
console.log(iterativeExpBySqWithMod(27725, 15583, 64523)); //65n

// Small numbers output a string
console.log(iterativeExpBySqWithMod(72552, -50102)); //"5.550317946486025e-243529"


朴素递归:

根据运行环境调整maxStack 参数:

function getCongruence(c, d, n, maxStack) {
  return getPow(BigInt(c), BigInt(d), BigInt(maxStack)) % BigInt(n);
}

const recursivePow = (value, exponent, total) => {
  if (exponent > 1) {
    exponent--;
    return recursivePow(value, exponent, total) * value;
  } else {
    if (total) {
      return total * value;
    } else {
      return value;
    }
  }
};

const getPow = (value, exponent, maxStack) => {
  let total = BigInt(0);

  while (exponent > maxStack) {
    total = recursivePow(value, maxStack, total);
    exponent -= maxStack;
  }
  return recursivePow(value, exponent, total);
};

console.log(getCongruence(27725, 15583, 64523, 3000)); //65n

【讨论】:

  • 感谢您的回答,我尝试运行它,但它不起作用
  • 这里在node上运行,你在node或浏览器中运行什么环境?
  • 这似乎比通常的平方迭代求幂更复杂
  • @the-breaker 好吧,我做了一些快速测试,它处理了相当大的数字,但也许它可以与 harold 提出的通过平方取幂的建议相结合以提高性能。
  • 我建议在求幂过程中也应用模减少,以防止数字变得非常大/慢
【解决方案2】:

我对 RSA 还不太了解,不过我正在努力学习它。

但至少我知道如何修复您的代码:

function getCongruence(c, d, n) {
  return c**d % n;
}

console.log(getCongruence(5,3,7)); // = 6 cuz 5^3=125 and 125 % 7 => 125 - 7*17 = 125 -119
console.log(getCongruence(19,11,17)); // = 8 cuz 19^11=116490258898219 % 17 = 8
console.log(getCongruence(27725n,15583n,64523n)); // now returns 65n (n as in BigInt)

但是哈罗德是这么说的(是真的吗?):

即使该函数有效,它仍然无法正常工作(哈哈),我的意思是,您不能将求幂和模约简分开,中间结果会变得太大,至少对于合理的消息和密钥大小而言。 ——哈罗德

关于其余代码,我无法让您的计算在 JS 中运行,我不知道为什么(但它在计算器中运行)?

console.log(65^127 % 64523) // 62 instead of 27725
console.log(27725^15583 % 64523) // 20626 instead of 65

// Trying the same with BigInt's:
console.log(65n^127n % 64523n) // 62 instead of 27725
console.log(27725n^15583n % 64523n) // 20626 instead of 65

// This works though:
console.log(65n**127n % 64523n) // 27725
console.log(27725n**15583n % 64523n) // 65

啊,我明白了。如果与现实世界的加密密钥一起使用,c**d 会在内存中生成一个超级大的数字,因此它将不起作用。但我想这会:

function getCongruence(c, d, n) {
  return powermod(c,d,n)
}
// from: https://stackoverflow.com/questions/30694842/exponentiation-by-squaring
function powermod(base, exponent, modulus) {
  if (base < 0n || exponent < 0n || modulus < 1n)
      return -1n
  let result = 1n
  while (exponent > 0n) {
    if ((exponent % 2n) == 1n) {
      result = (result * base) % modulus
    }
    log(base * base)
    base = (base * base) % modulus
    exponent = exponent / 2n // floor
  }
  return result
}

或者也许使用 PeterN 的那个(上图),像这样:

function getCongruence(c, d, n) {
  return iterativeExpBySqWithMod(c,d,n)
}

【讨论】:

    猜你喜欢
    • 2014-06-19
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 2012-01-16
    • 1970-01-01
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多