次のRSAキーが与えられた場合、pおよびqの値が何であるかをどのように判断しますか?
Public Key: (10142789312725007, 5)
Private Key: (10142789312725007, 8114231289041741)
あなたの先生はあなたに与えた:
公開鍵:(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
(「m」の値が小さい場合はnを超えないため、「e」は実際にはもっと大きくする必要があります)
とにかく、今「c」があり、「d」でそれを逆にすることができます
m = c^d mod n
= 7487844069764171^8114231289041741 mod 10142789312725007
= 123456789
明らかに、「7487844069764171 ^ 8114231289041741」は128,808,202,574,088,302桁であるため、直接計算することはできません。したがって、 モジュラーべき乗 トリックを使用する必要があります。
「実世界」では、nは明らかにはるかに大きいです。 HTTPSが617桁のnおよびeの65537、私のブログ投稿「 HTTPS接続の最初の数ミリ秒 」を参照してください。
これを見るのは比較的簡単な方法です(手で実行できる方法)。数値を完全に因数分解する場合、考慮する必要がある最高の要因はsqrt(N)です。
sqrt(10142789312725007) = 100711415.9999997567
これより下の最初の素数は100711409で、sqrt(N)の6つ下にあります。
10142789312725007 / 100711409 = 100711423
したがって、これらはNの2つの要因です。あなたの教授はそれを非常に簡単にしました-トリックは誰もsmall pまたはqを選択しないことを認識するので、下からチェックを開始します(python誰かが投稿したスクリプト)は悪い考えです。手作業で実用的になる場合、大きなpとqはsqrt(N)の近くになければなりません。
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
これは大幅に改善される可能性がありますが、問題なく機能します。素因数をテストするだけで改善できますが、あなたのような小さな値の場合はこれで十分です。
Java Applied Cryptography Handbookの高速因数分解メソッドの実装 8章 セクション8.2.2(見つけた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)
RSAの定義は、モジュラスn = pq
。 n
を知っているので、乗算してp
を生成する2つの数値q
とn
を見つける必要があります。 p
とq
が素数であることがわかっているので、これが素因数分解問題です。
比較的小さな数に対しては総当たりでこれを解決できますが、RSAの全体的なセキュリティは、一般にこの問題が扱いにくいという事実に依存します。
これらの2つの論文はおそらく役に立つかもしれません
私は継続的な分数に関するいくつかの基本的な研究をしていたときにそれらに出くわした。
これを行うアルゴリズムは次のとおりです(これはどのコンピューターでも簡単にファクタリングできるこの小さなものだけでなく、どの例でも機能します)。
ed - 1
はphi(n) = (p-1)(q-1)
の倍数なので、少なくとも4の倍数です。ed - 1
は、2^7 * 316962159728193
に等しい40571156445208704として計算でき、s=7
およびt = 316962159728193
を呼び出します。 (一般的に、偶数は奇数の2のべき乗です)。ここで、[2,n-1)
をランダムに選択し、(n
を法とする連続した2乗法により)a^t (mod n), a^(2t) (mod n), a^(4t) (mod n)..
シーケンスを最大a^((2^7)*t) (mod n)
まで計算します。最後のシーケンスは、e
およびd
の構築により、1.
次に、そのシーケンスの最初の1を探します。その前のものは+1
または-1
(1、mod n
のささいなルート)であり、別のa、または+1
または-1
mod n
と等しくないいくつかの番号x
でやり直します。後者の場合、gcd(x-1, n)
はn
の非自明な除数であるため、p
またはq
であり、完了です。ランダムなaが約0.5の確率で機能することを示すことができるので、数回の試行が必要ですが、一般的にはそれほど多くありません。
ネクロマンシーで申し訳ありませんが、友人がこれについて私に尋ねました、そして、彼をここに指した後、私は答えのどれも本当に好きではないことに気づきました。モジュラスを因数分解して素数(pおよびq)を取得した後、(p-1)*(q-1)
であるtotientを見つける必要があります。
ここで、プライベート指数を見つけるために、パブリック指数の逆数を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);
}
私が使用した因数分解アルゴリズムは愚かですが、簡潔なので、そこに一粒の粒があります。この特定の例では、コードはほぼ瞬時に実行されますが、これは主に、問題のインストラクターが2つの素数を連続して使用する例を提供したためです。これはRSAにとって現実的ではありません。
愚かな反復検索を排除したい場合は、実際の因数分解アルゴリズムを使用し、妥当な時間内に最大で約256ビットのキーを因数分解できます。
Quadratic Sieve について読むことをお勧めします。自分で実装する場合、これは間違いなく価値があります。原則を理解していれば、すでに何かを得ています。
モジュラスを因数分解する必要があります。これは公開鍵の最初のパラメータ10142789312725007です。ブルートフォースは実行します(因数であれば3からsqrt(n)までの奇数をすべてチェックします)。
数値が大きすぎて従来の整数(64ビットでも)に収まらないため、任意の長さの整数をサポートする数値ライブラリが必要になる場合があります。 Cには、GMPとMPIR(よりWindowsフレンドリー)があります。 PHPには、Bignumがあります。 Pythonには組み込みのものが付属しています-組み込み整数データ型は既に任意の長さです。
大規模な準素数の素因数分解またはふるい分けのいずれについても半素数の素因数分解に必要な因子分解について多くの悪い推測があります。私のPCでは64ビットは1〜2秒かかり、一般的に256ビットは2日未満です