【问题标题】:Cracking short RSA keys破解短 RSA 密钥
【发布时间】:2011-05-03 23:53:28
【问题描述】:

给定以下 RSA 密钥,如何确定 pq 的值是什么?

Public Key: (10142789312725007, 5)
Private Key: (10142789312725007, 8114231289041741)

【问题讨论】:

  • 到目前为止您尝试过哪些算法?如果这是针对数论课程的,那么您的文本中应该有一两章是关于加密算法的。
  • 这是一个信息安全课程。教授没有解释如何做到这一点,但提供了一点额外的学分。我还没有尝试过任何算法,因为我不确定如何处理它。
  • 在给定私钥的情况下,有一种快速分解n的方法。
  • 注意:我不是在这里寻求解决问题,而是试图发现如何执行必要的计算。
  • @DuncanJones:大约十年前问这个问题时,crypto.stackexchange.com 存在吗?

标签: math rsa encryption-asymmetric public-key-encryption


【解决方案1】:

你的老师给你的:

公钥:(10142789312725007, 5)

意思是

n = 10142789312725007
e = 5 

其中 n 是模数,e 是公共指数。

另外,给你

私钥:(10142789312725007, 8114231289041741)

意思是

 d = 8114231289041741

其中 d 是应该保密的解密指数。

您可以通过了解如何将“n”分解为“p”和“q”质因数来“破解”RSA:

n = p * q

最简单的方法可能是检查从 n 的平方根以下开始的所有奇数:

Floor[Sqrt[10142789312725007]] = 100711415

您将在 4 次尝试中获得第一个因素:

10142789312725007 mod 100711415 = 100711367
10142789312725007 mod 100711413 = 100711373
10142789312725007 mod 100711411 = 100711387
10142789312725007 mod 100711409 = 0 <-- Winner since it evenly divides n

所以我们有

 p = 100711409

现在,

 q = n / p 
   = 10142789312725007 / 100711409
   = 100711423

为什么这很重要?这是因为 d 是一个特殊的数字,因此

d = e^-1 mod phi(n)
  = e^-1 mod (p-1)*(q-1)

我们可以验证这一点

d * e = 40571156445208705 = 1 mod 10142789111302176

这很重要,因为如果你有一个明文消息 m 那么密文就是

c = m^e mod n

然后你解密它

m = c^d = (m^e)^d = (m^(e*d)) = (m^(e*e^-1)) = m^1 (mod n)

例如,我可以使用您老师的公钥“加密”消息 123456789:

m = 123456789

这会给我以下密文:

c = m^e mod n 
  = 123456789^5 mod 10142789312725007
  = 7487844069764171

(请注意,实际上“e”应该大得多,因为对于较小的“m”值,您甚至不会超过 n)

不管怎样,现在我们有了 "c" 并且可以用 "d" 反转它

m = c^d mod n
  = 7487844069764171^8114231289041741 mod 10142789312725007
  = 123456789

显然,您不能直接计算“7487844069764171^8114231289041741”,因为它有 128,808,202,574,088,302 位,因此您必须使用 modular exponentiation 技巧。

在“现实世界”中,n显然要大得多。如果您想查看 HTTPS 如何在幕后使用 RSA 的真实示例,其 n 为 617 位,e 为 65537,请参阅我的博文“@ 987654322@。”

【讨论】:

  • 这仍然是一个蛮力解决方案,不适用于更大的数字。
  • 是的,对于较大的,您需要像 Number Field Sieve 这样的东西。我只是想为这个特定问题提供一些实用的东西,你可以用 calc.exe 来做 :)
  • 嗨。我只是在阅读您如何使用以下方法找到这些因素:“Floor[Sqrt[10142789312725007]] = 100711415” 我只是想知道,如果其中一个因素小到 5 怎么办?那么你的解决方案就行不通了,不是吗?我知道应该避免小因素,但我想它们可能会出现。
  • @Aerovistae 我使用了 WolframAlpha。
  • 对编程人员的重要说明。确保Floor[Sqrt[n]] 的结果是int 数据类型。许多语言需要显式转换!将其保留为浮点数可能会导致程序的执行时间延长数百倍。
【解决方案2】:

这是一种相对简单的查看方法(并且可以手动完成)。如果你要完全分解这个数字,那么你需要考虑的最高因素是 sqrt(N):

sqrt(10142789312725007) = 100711415.9999997567

它下面的第一个素数是 100711409,仅比 sqrt(N) 低 6 个。

10142789312725007 / 100711409 = 100711423 

因此这是 N 的两个因数。你的教授让它变得很容易 - 诀窍是认识到没有人会选择 p 或 q 所以从底部开始检查(如有人发布的python脚本)是个坏主意。如果要手动实用,大的 p 和 q 必须位于 sqrt(N) 附近。

【讨论】:

  • 你是对的,从最大值开始看起来是一个更好的方法。我没想到。
【解决方案3】:

在给定ned 的情况下,有多种快速算法可以解决n 的因式分解问题。您可以在《应用密码学手册》Chapter 8 第 8.2.2 节中找到对此类算法的一个很好的描述。您可以在线找到这些章节并免费下载here。该算法本质上是Henno Brandsma's answer对这个问题的仔细阐述。

2019 年 9 月 25 日更新:

comment below 中,用户Imperishable Night 提出了一种至少在概念上更容易理解的替代方法。

他指出,e 通常很小。事实上e 几乎总是 65537。如果e 很小,您可以在未知素数p 中开发一个二次方程,从而使用例如轻松求解它。 the quadratic formula。为了继续,让我们设置 x=p 并求解x,只是为了遵守约定。我们知道ed = 1 mod phi(n),或者等价的 ed - 1 = k * (p-1)*(q-1)。现在设置x=p,因此设置n/x=q,将两边乘以x并重新排列我们拥有的术语
k*x2 + (d*e - k*n - k - 1)*x + k*n = 0。
现在我们有一个方程 形式为 ax2 + bx + c = 0,我们可以使用二次公式求解 x。所以我们可以在e 附近的一个小范围内尝试k 的值,并且二次方应该只有一个整数解,即正确k 的解。

注意事项:

  1. 一切都必须是整数,因此判别式必须是完全平方,否则我们可以丢弃 k 并尝试下一个。此外,分子必须能被 2*k 整除。
  2. 有时使用 Carmichael lambda 函数代替 Euler phi 函数。这使事情稍微复杂了一点,因为我们现在还必须猜测g = gcd(p-1, q-1)g 总是偶数,通常是 2,否则几乎总是 2 的小倍数。

2019 年 9 月 26 日更新:

e 很小时,查找k 实际上非常容易。通过取方程 ed - 1 = k * (p-1)*(q-1) 两边除以n 很容易看出floor((ed-1)/n) + 1 == k。现在使用方程 M.J. Wiener's "Cryptanalysis of Short RSA Secret Exponents"的31和32个可以直接恢复pq

【讨论】:

  • 我认为当e 很小时,有一个概念上更简单的解决方案,这在RSA 的实际应用中通常是这种情况。在这些情况下,ed-1(p-1)(q-1) 的小倍数,应该非常接近n,所以你可以暴力破解(p-1)(q-1) 的所有合理值,从中和n=pq 你可以解决一个找到pq 的简单二次方程组。
  • @ImperishableNight:我终于有时间检查你的评论,我必须同意你的观点,你的方法在概念上更简单。我将编辑我的答案以包含您的方法。
【解决方案4】:

Wolframalpha 告诉我因子是 100711409 和 100711423

我刚刚编写了一个简单的 Python 脚本来暴力破解它。 正如 amdfan 所指出的,从顶部开始是一种更好的方法:

p = 10142789312725007
for i in xrange(int(p**0.5+2), 3, -2):
    if p%i == 0:
        print i
        print p/i
        break

这可以大大改进,但它仍然可以正常工作。您可以通过仅测试 primfactors 来改进它,但对于像您这样的小值,这应该足够了。

【讨论】:

  • 仅仅提供答案对 OP 没有帮助...... Wolframalpha 肯定不会在测试中提供给他/她。
  • 嗯,这肯定给了我答案!如果没有其他人解释如何手动执行此操作,我会给你一个绿色的复选标记。
  • StackOverflow 不是一个巨大的计算器网站。该网站的目标是帮助人们了解如何做事。这不是您要求人们为您编码或计算的地方。
  • @Juri 作业标签应该已经告诉你,无论 OP 要求什么,只提供因素并不是最佳实践。
  • 所有问题都应该进行评估,以了解如何最好地回答它们——请注意,Silence 的评论并没有将作业与其他问题区分开来。如果发帖人需要提供更多信息(即这个问题很稀疏,不管你怎么切),那么你必须问。标记作业不会告诉你任何事情。例如,如果我问这个问题(而且我肯定不再做作业了),我想要的是解释而不是两个因素。
【解决方案5】:

RSA 的定义告诉你模数n = pq。你知道n,所以你只需要找到两个数字pq,它们相乘产生n。你知道pq 是素数,所以这就是素数分解问题。

对于相对较小的数字,您可以通过蛮力解决此问题,但 RSA 的整体安全性取决于此问题通常难以解决的事实。

【讨论】:

  • 不是真的!当给定解密指数时,就像在这种情况下,问题很简单。
  • 好吧,当给定解密指数时,你就有了私钥,这有点违背了目的。知道d 会使分解更容易吗?如果有,你能解释一下吗?
【解决方案6】:

这里是《应用密码学手册》chapter 8 第 8.2.2 节中快速分解方法的 Java 实现(感谢 GregS 的发现):

/**
 * Computes the factors of n given d and e.
 * Given are the public RSA key (n,d)
 * and the corresponding private RSA key (n,e).
 */
public class ComputeRsaFactors
{
    /**
     * Executes the program.
     *
     * @param args  The command line arguments.
     */
    public static void main(String[] args)
    {
        final BigInteger n = BigInteger.valueOf(10142789312725007L);
        final BigInteger d = BigInteger.valueOf(5);
        final BigInteger e = BigInteger.valueOf(8114231289041741L);

        final long t0 = System.currentTimeMillis();

        final BigInteger kTheta = d.multiply(e).subtract(BigInteger.ONE);
        final int exponentOfTwo = kTheta.getLowestSetBit();

        final Random random = new Random();
        BigInteger factor = BigInteger.ONE;
        do
        {
            final BigInteger a = nextA(n, random);

            for (int i = 1; i <= exponentOfTwo; i++)
            {
                final BigInteger exponent = kTheta.shiftRight(i);
                final BigInteger power = a.modPow(exponent, n);

                final BigInteger gcd = n.gcd(power.subtract(BigInteger.ONE));
                if (!factor.equals(BigInteger.ONE))
                {
                    break;
                }
            }
        }
        while (factor.equals(BigInteger.ONE));

        final long t1 = System.currentTimeMillis();

        System.out.printf("%s %s (%dms)\n", factor, n.divide(factor), t1 - t0);
    }


    private static BigInteger nextA(final BigInteger n, final Random random)
    {
        BigInteger r;
        do
        {
            r = new BigInteger(n.bitLength(), random);
        }
        while (r.signum() == 0 || r.compareTo(n) >= 0);
        return r;
    }
}

典型的输出是

100711423 100711409 (3ms)

【讨论】:

    【解决方案7】:

    这两篇论文可能有用

    当我对连分数进行一些基础研究时遇到了它们。

    【讨论】:

      【解决方案8】:

      执行此操作的算法是(这适用于任何示例,不仅是可以被任何计算机轻松分解的小示例):

      ed - 1phi(n) = (p-1)(q-1) 的倍数,因此至少是 4 的倍数。
      ed - 1 可以计算为 40571156445208704,它等于 2^7 * 316962159728193, 我们打电话给s=7t = 316962159728193。 (一般来说:任何偶数都是奇数的 2 次方)。 现在在[2,n-1) 中随机选择一个,并计算(通过连续平方模n)序列 a^t (mod n), a^(2t) (mod n), a^(4t) (mod n).. 至多a^((2^7)*t) (mod n), 通过ed 的构造,最后一个保证为1。

      我们现在查找该序列中的第一个 1。前一个将是+1-1 (一个微不足道的根 1,mod n)然后我们用不同的 a 或某个不等于 +1-1 mod n 的数字 x 重做。 在后一种情况下,gcd(x-1, n)n 的非平凡除数,因此pq,我们就完成了。可以证明一个随机的 a 会以大约 0.5 的概率起作用,所以我们需要尝试几次,但一般不会很多。

      【讨论】:

        【解决方案9】:

        对于死灵术,我很抱歉,但一位朋友问了我这个问题,在将他指向这里之后,我意识到我并不真正喜欢任何答案。在分解模数并得到素数(p和q)后,您需要找到totient,即(p-1)*(q-1)

        现在,要找到私有指数,您需要找到公共指数的倒数 mod the totient。

        public_exponent * private_exponent = 1 mod totient
        

        现在你有了你的私钥,就这么简单。对于大整数,除了因式分解之外的所有这些几乎都可以立即完成。

        我写了一些代码:

        // tinyrsa.c
        //
        // apt-get install libgmp-dev
        // yum install gmp-devel
        //
        // gcc tinyrsa.c -o tinyrsa -lm -lgmp
        
        #include<stdio.h>
        #include<gmp.h>
        
        int main()
        {
          // declare some multi-precision integers
          mpz_t pub_exp, priv_exp, modulus, totient, fac_p, fac_q, next_prime;
        
          mpz_init_set_str(pub_exp,"5",10);
          mpz_init_set_str(modulus,"10142789312725007",10);
        
          mpz_init(priv_exp);
          mpz_init(totient);
          mpz_init(fac_p);
          mpz_init(fac_q);
        
          // now we factor the modulus (the hard part)
          mpz_init(next_prime);
          mpz_sqrt(next_prime,modulus);
          unsigned long removed=0;
          while(!removed)
          {
            mpz_nextprime(next_prime,next_prime);
            removed=mpz_remove(fac_p,modulus,next_prime);
          }
        
          mpz_remove(fac_q,modulus,fac_p);
          // we now have p and q
        
          // the totient is (p-1)*(q-1)  
          mpz_t psub, qsub;
          mpz_init(psub);
          mpz_init(qsub);
        
          mpz_sub_ui(psub,fac_p,1);
          mpz_sub_ui(qsub,fac_q,1);
          mpz_mul(totient,psub,qsub);
        
          // inverse of the public key, mod the totient..
          mpz_invert(priv_exp,pub_exp,totient);
        
          gmp_printf("private exponent:\n%Zd\n",priv_exp);
        
        }
        

        我使用的分解算法很愚蠢,但很简洁,所以这里有一点盐。在这个特定示例中,代码几乎可以立即运行,但这主要是因为所讨论的讲师提供了一个连续使用两个素数的示例,这对于 RSA 来说是不现实的。

        如果你想去掉我愚蠢的迭代搜索,你可以使用一些真正的分解算法,并在合理的时间内分解可能高达 256 位的密钥。

        【讨论】:

        • 他在他的问题中得到了私有指数。在不使用附加信息的情况下因式分解 n 不适用于实际 RSA 系统中的大整数。
        【解决方案10】:

        我建议您阅读有关Quadratic Sieve 的信息。如果你自己实现一个,这肯定是值得的。如果你理解了这些原则,你就已经有所收获了。

        【讨论】:

          【解决方案11】:

          您需要分解模数,这是公钥的第一个参数 10142789312725007。蛮力会做(检查从 3 到 sqrt(n) 的每个奇数,如果它是一个因数),尽管存在更复杂/快速的算法.

          由于数字太大而无法放入常规整数(即使是 64 位),您可能需要一个支持任意长度整数的数字库。对于 C,有 GMP 和 MPIR(对 Windows 更友好)。对于 PHP,有 Bignum。 Python 自带一个内置的——内置的整数数据类型已经是任意长度的。

          【讨论】:

            【解决方案12】:

            关于大半素数的因式分解有很多不好的猜测,它们会进入蛮力或筛分,这两者都不需要分解半素数。在我的电脑上 64 位需要 1 - 2 秒,而 256 位通常不到 2 天

            【讨论】:

            • 你能解释一下你是怎么做的吗?
            • 我会,但直到我破解了 RSA 挑战号码。目前,我正试图了解 c+、cuda 和视觉工作室。我天生就是一个帕斯卡程序员。同时,如果您需要 64 位破解,请给我发送一封包含详细信息的电子邮件。
            猜你喜欢
            • 2020-08-11
            • 2017-12-13
            • 2012-05-08
            • 2018-12-10
            • 2021-10-03
            • 2018-09-10
            • 1970-01-01
            • 2020-02-17
            • 1970-01-01
            相关资源
            最近更新 更多