Étant donné les clés RSA suivantes, comment peut-on déterminer les valeurs de p et q?
Public Key: (10142789312725007, 5)
Private Key: (10142789312725007, 8114231289041741)
Votre professeur vous a donné:
Clé publique: (10142789312725007, 5)
ce qui signifie
n = 10142789312725007
e = 5
où n est le module et e est l'exposant public.
De plus, on vous donne
Clé privée: (10142789312725007, 8114231289041741)
ce qui signifie que
d = 8114231289041741
où d est l'exposant de déchiffrement qui doit rester secret.
Vous pouvez "casser" le RSA en sachant comment factoriser "n" en ses facteurs premiers "p" et "q":
n = p * q
La façon la plus simple est probablement de vérifier tous les nombres impairs commençant juste en dessous de la racine carrée de n:
Floor[Sqrt[10142789312725007]] = 100711415
Vous obtiendrez le premier facteur en 4 essais:
10142789312725007 mod 100711415 = 100711367
10142789312725007 mod 100711413 = 100711373
10142789312725007 mod 100711411 = 100711387
10142789312725007 mod 100711409 = 0 <-- Winner since it evenly divides n
Nous avons donc
p = 100711409
À présent,
q = n / p
= 10142789312725007 / 100711409
= 100711423
Pourquoi est-ce important? C'est parce que d est un nombre spécial tel que
d = e^-1 mod phi(n)
= e^-1 mod (p-1)*(q-1)
Nous pouvons le vérifier
d * e = 40571156445208705 = 1 mod 10142789111302176
Ceci est important car si vous avez un message en clair m , le texte chiffré est
c = m^e mod n
et vous le décryptez par
m = c^d = (m^e)^d = (m^(e*d)) = (m^(e*e^-1)) = m^1 (mod n)
Par exemple, je peux "crypter" le message 123456789 en utilisant la clé publique de votre professeur:
m = 123456789
Cela me donnera le texte chiffré suivant:
c = m^e mod n
= 123456789^5 mod 10142789312725007
= 7487844069764171
(Notez que "e" devrait être beaucoup plus grand en pratique car pour les petites valeurs de "m" vous ne dépassez même pas n)
Quoi qu'il en soit, nous avons maintenant "c" et pouvons l'inverser avec "d"
m = c^d mod n
= 7487844069764171^8114231289041741 mod 10142789312725007
= 123456789
De toute évidence, vous ne pouvez pas calculer directement "7487844069764171 ^ 8114231289041741" car il contient 128 808 202 202 744 088 302 chiffres, vous devez donc utiliser l'astuce exponentiation modulaire .
Dans le "monde réel", n est évidemment beaucoup plus grand. Si vous souhaitez voir un exemple réel de la façon dont HTTPS utilise RSA sous les couvertures avec un 617 chiffres n et un e de 65537, voir mon article de blog " Les premières quelques millisecondes d'une connexion HTTPS ."
Voici une façon relativement simple de le voir (et qui est faisable à la main). Si vous deviez factoriser le nombre complètement, alors le facteur le plus élevé que vous devriez considérer est sqrt (N):
sqrt(10142789312725007) = 100711415.9999997567
Le premier nombre premier en dessous est 100711409, juste 6 en dessous du sqrt (N).
10142789312725007 / 100711409 = 100711423
par conséquent, ce sont deux facteurs de N. Votre professeur a rendu les choses assez faciles - l'astuce consiste à reconnaître que personne ne choisirait un petit p ou q afin de commencer votre vérification par le bas (comme dans le python script que quelqu'un a posté) est une mauvaise idée. Si cela doit être pratique à la main, les grands p et q doivent se trouver près de sqrt (N).
Il existe différents algorithmes rapides pour résoudre le problème de factorisation n
étant donné n
, e
et d
. Vous pouvez trouver une bonne description d'un tel algorithme dans le Handbook of Applied Cryptography, Chapter 8 , section 8.2.2. Vous pouvez trouver ces chapitres en ligne pour téléchargement gratuit ici .
Wolframalpha me dit que les facteurs sont 100711409 et 100711423
Je viens d'écrire un script naïf Python pour le forcer brutalement. Comme l'a souligné amdfan, partir du haut est une meilleure approche:
p = 10142789312725007
for i in xrange(int(p**0.5+2), 3, -2):
if p%i == 0:
print i
print p/i
break
Cela pourrait être considérablement amélioré, mais cela fonctionne toujours sans problème. Vous pouvez l'améliorer en testant simplement les primfacteurs, mais pour de petites valeurs comme la vôtre, cela devrait suffire.
Voici une Java implémentation de la méthode de factorisation rapide du Handbook of Applied Cryptography chapitre 8 section 8.2.2 (merci à GregS de l'avoir trouvée):
/**
* 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;
}
}
Une sortie typique est
100711423 100711409 (3ms)
La définition de RSA vous indique que le module n = pq
. Vous connaissez n
donc il vous suffit de trouver deux nombres p
et q
qui se multiplient pour produire n
. Vous savez que p
et q
sont premiers, c'est donc le problème de la factorisation principale.
Vous pouvez résoudre ce problème par la force brute pour des nombres relativement petits, mais la sécurité globale de RSA dépend du fait que ce problème est insoluble en général.
Ces deux documents pourraient être utiles
Je les ai rencontrés lorsque je faisais des recherches fondamentales sur les fractions continues.
L'algorithme pour ce faire est (et cela fonctionnera pour n'importe quel exemple, pas seulement ce petit qui peut être facilement factorisé par n'importe quel ordinateur):
ed - 1
Est un multiple de phi(n) = (p-1)(q-1)
, donc au moins un multiple de 4.ed - 1
Peut être calculé comme 40571156445208704 qui équivaut à 2^7 * 316962159728193
, Et nous appelons s=7
Et t = 316962159728193
. (en général: tout nombre pair est une puissance de 2 fois un nombre impair). Maintenant, choisissez un dans [2,n-1)
Au hasard et calculez (par quadrature successive modulo n
) la séquence a^t (mod n), a^(2t) (mod n), a^(4t) (mod n)..
jusqu'à au plus a^((2^7)*t) (mod n)
, où le dernier on est garanti être 1, par la construction de e
et d
.
Nous recherchons maintenant le premier 1 de cette séquence. Celui qui le précède sera soit +1
Ou -1
(Une racine triviale de 1, mod n
) Et nous refaisons avec un différent a ou un certain nombre x
qui n'est pas égal à +1
ou -1
mod n
. Dans ce dernier cas, gcd(x-1, n)
est un diviseur non trivial de n
, et donc p
ou q
, et nous avons terminé. On peut montrer qu'un a aléatoire fonctionnera avec une probabilité d'environ 0,5, nous avons donc besoin de quelques essais, mais pas beaucoup en général.
Désolé pour la nécromancie, mais un ami m'a posé des questions à ce sujet, et après l'avoir pointé ici, j'ai réalisé que je n'aimais pas vraiment les réponses. Après avoir factorisé le module et obtenu les nombres premiers (p et q), vous devez trouver le totient, qui est (p-1)*(q-1)
.
Maintenant, pour trouver l'exposant privé, vous trouvez l'inverse de l'exposant public mod le totient.
public_exponent * private_exponent = 1 mod totient
Et maintenant, vous avez votre clé privée, aussi simple que cela. Tout cela, à l'exception de la factorisation, peut être effectué presque instantanément pour les entiers énormes.
J'ai écrit du code:
// 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);
}
L'algorithme de factorisation que j'ai utilisé est stupide, mais concis, donc grain de sel là-bas. Dans cet exemple particulier, le code s'exécute presque instantanément, mais c'est en grande partie parce que l'instructeur en question a fourni un exemple qui utilise deux nombres premiers consécutifs, ce qui n'est pas vraiment réaliste pour RSA.
Si vous vouliez couper ma stupide recherche itérative, vous pourriez mettre en place un véritable algorithme de factorisation, et des clés de facteur probablement jusqu'à environ 256 bits dans un laps de temps raisonnable.
Je vous suggère de lire sur le tamis quadratique . Si vous en implémentez un vous-même, cela vaut certainement le coup. Si vous comprenez les principes, vous avez déjà gagné quelque chose.
Vous devez factoriser le module, c'est le premier paramètre de la clé publique, 10142789312725007. La force brute fera l'affaire (vérifiez chaque nombre impair de 3 à sqrt (n) si c'est un facteur), bien qu'il existe des algorithmes plus sophistiqués/rapides.
Étant donné que le nombre est trop grand pour tenir dans un entier conventionnel (même 64 bits), vous souhaiterez peut-être une bibliothèque numérique qui prend en charge les entiers de longueur arbitraire. Pour C, il y a GMP et MPIR (plus convivial pour Windows). Pour PHP, il y a Bignum. Python est livré avec un code intégré - le type de données entier intégré est déjà de longueur arbitraire.
Il y a beaucoup de mauvaises spéculations sur la factorisation des grands semi-premiers qui entrent en force brute ou le tamisage dont aucun n'est requis pour factoriser le semi-premier. 64 bits prennent 1 à 2 secondes sur mon ordinateur et 256 bits généralement moins de 2 jours