Criptografie cu cheie asimetrică
În această lecție vom explora criptografia cu cheie asimetrică, care stă la baza multor interacțiuni de rețea securizate din ziua de azi.
Până la sfârșitul lecției, vom fi acoperit:
- Ce este criptografia cu cheie asimetrică
- Utilizările criptografiei cu cheie asimetrică, inclusiv schimbul de chei și semnăturile digitale
- Securitatea criptografiei cu cheie asimetrică în general
- Detalii suplimentare despre algoritmii RSA, DSA și Curbe Eliptice și securitatea lor
- Câteva exemple de cod Python care arată cum funcționează algoritmii în practică
- Amenințări la adresa acestor algoritmi atât din partea calculatoarelor clasice, cât și a celor cuantice
Introducere în criptografia cu cheie asimetrică
Așa cum am aflat în lecția precedentă, criptografia cu cheie simetrică este foarte rapidă și eficientă pentru protejarea informațiilor, dar are câteva limitări:
- Pe măsură ce numărul de părți care doresc să facă schimb de informații securizate crește, numărul de chei necesar crește combinatorial. Nu există niciun mecanism de distribuire securizată a acestor chei între expeditori și destinatari.
- Nu există nicio prevedere pentru non-repudiere. Orice parte poate decripta sau cripta mesaje, fără nicio modalitate de a garanta că un mesaj a fost primit sau de unde a provenit.
Soluția la ambele probleme este oferită de criptografia cu cheie asimetrică (AKC), cunoscută și sub denumirea de criptografie cu cheie publică (PKC), care formează astfel o piatră de temelie a securității digitale moderne.
Criptografia cu cheie asimetrică (AKC) implică utilizarea unei perechi de chei — una publică, una privată. Cheia publică și cea privată sunt legate criptografic și sunt generate de obicei în același timp ca o pereche de chei, folosind un algoritm matematic specializat. Cheia publică, după cum sugerează și numele, este menită să fie distribuită liber, în timp ce cheia privată este păstrată secretă de către partea care a generat perechea de chei. Securitatea comunicațiilor care utilizează perechi de chei asimetrice este asigurată atâta timp cât cheia privată rămâne confidențială.

Figura 1. Criptare cu cheie asimetrică
AKC oferă mai multe funcții utile, cum ar fi:
- Criptare și decriptare pentru a asigura confidențialitatea comunicațiilor.
- Semnături digitale pentru a garanta autenticitatea, integritatea și non-repudierea.
- Schimb securizat de chei pentru a facilita utilizarea ulterioară a criptosistemelor simetrice.
În aplicațiile moderne, AKC este utilizat în principal pentru semnături digitale și schimb securizat de chei. În această lecție, prezentăm aceste două funcții cheie, apoi discutăm mai multe variante de protocoale criptografice pentru aceste funcții.
Schimbul de chei cu criptografia cu cheie asimetrică
Una dintre problemele fundamentale în criptografie este schimbul securizat de chei. De exemplu, dacă două părți doresc să utilizeze criptarea simetrică, ambele au nevoie de aceeași cheie pentru a cripta și decripta mesajele. Dar cum fac schimbul securizat al cheii? Criptografia cu cheie asimetrică abordează această problemă prin mecanisme de schimb de chei interactive și non-interactive.
Schimb de chei interactiv
Un protocol de schimb de chei interactiv se referă la o metodă prin care două părți colaborează pentru a crea o cheie secretă partajată printr-un canal de comunicație nesecurizat. Această cheie secretă partajată poate fi apoi utilizată pentru sarcini de criptare și decriptare simetrică.
Cel mai cunoscut dintre astfel de protocoale este algoritmul Diffie-Hellman (DH), conceput special pentru a facilita schimbul de chei. În acest protocol, fiecare parte generează o pereche de chei (publică și privată) și transmite cheia sa publică. Apoi, fiecare parte utilizează propria cheie privată și cheia publică a celeilalte părți pentru a genera o cheie secretă partajată. DH folosește principiile aritmeticii modulare pentru a garanta că ambele părți ajung la același secret partajat, chiar dacă fiecare parte are acces doar la cheia publică a celeilalte.
Criptosistemele moderne bazate pe criptografia cu curbe eliptice (ECC) extind acest concept cu schimbul de chei Diffie-Hellman pe curbe eliptice (ECDH). ECDH funcționează similar cu DH, dar utilizează proprietățile curbelor eliptice, rezultând un sistem mai sigur și mai eficient.

Figura 2. Protocol de schimb de chei
Schimb de chei non-interactiv
Spre deosebire de protocoalele de schimb de chei precum DH și ECDH, care sunt interactive și necesită comunicare în ambele sensuri pentru a determina cheia simetrică, AKC oferă și modalități non-interactive de a stabili o cheie secretă partajată. În astfel de scheme, o parte generează o pereche de chei (publică și privată) și partajează cheia publică cu cealaltă parte. Cea de-a doua parte generează apoi o cheie simetrică aleatorie, o criptează cu cheia publică primită și o trimite înapoi primei părți. Prima parte utilizează cheia sa privată pentru a decripta mesajul primit, obținând astfel cheia simetrică partajată. Această schemă este non-interactivă în sensul că cheia simetrică este determinată de o singură parte și pur și simplu comunicată în mod securizat celeilalte părți în formă criptată.
O considerație importantă în schimbul de chei non-interactiv ține de diferența în biți dintre cheia simetrică pe care părțile doresc să o schimbe și dimensiunile de mesaj recomandate în AKC. De obicei, cheile simetrice moderne au între 128-256 biți, în timp ce criptosistemele cu cheie asimetrică precum RSA lucrează cu dimensiuni de mesaj de aproximativ 1024-4096 biți. Prin urmare, atunci când se utilizează AKC pentru a transmite o cheie simetrică, aceasta trebuie totuși codificată într-un mesaj mai lung de 1024-4096 biți. Acest lucru poate fi realizat prin două abordări:
-
Schimb de chei bazat pe padding: În această abordare, cheia simetrică mai scurtă (128-256 biți) este generată mai întâi, iar apoi o schemă de padding reversibilă convenită, cum ar fi OAEP, este utilizată pentru a o încorpora într-un mesaj mai lung (1024-4096 biți). Acest mesaj mai lung este criptat folosind AKC și transmis ca text cifrat. Destinatarul decriptează mai întâi textul cifrat, apoi elimină padding-ul pentru a extrage cheia simetrică mai scurtă.
-
Mecanisme de încapsulare a cheilor (KEM): Cu schimbul de chei bazat pe KEM, se generează mai întâi un mesaj în text clar aleatoriu și lung (1024-4096 biți), din care o cheie simetrică mai scurtă (128-256 biți) poate fi extrasă folosind o funcție de derivare a cheilor (KDF) convenită. Textul clar mai lung este criptat folosind AKC și transmis destinatarului ca text cifrat. Destinatarul decodifică textul cifrat folosind cheia sa privată și apoi utilizează KDF pentru a extrage cheia simetrică mai scurtă (128-256 biți). Criptosisteme populare precum RSA, cu capacitatea lor de a cripta direct date, pot fi folosite pentru a implementa KEM-uri.

Figura 3. Mecanism de încapsulare a cheilor
Semnături digitale cu criptografia cu cheie asimetrică
Semnăturile digitale reprezintă o altă aplicație puternică a criptografiei cu cheie asimetrică. Ele asigură autentificarea, integritatea și non-repudierea, posibile datorită faptului că în AKC, entitățile posedă chei private unice. Ideea de bază a protocoalelor de semnătură este că expeditorii de mesaje securizate vor semna digital mesajele folosind cheia lor privată unică. Destinatarul va verifica apoi semnătura digitală folosind cheia publică a expeditorului. În AKC, semnăturile digitale pot fi implementate folosind algoritmi special concepuți în acest scop sau prin utilizarea criptosistemelor generice.

Figura 4. Semnături digitale cu criptografia cu cheie asimetrică
Algoritmi dedicați pentru semnături digitale
În prezent, standardul US Federal Information Processing Standard (FIPS) pentru semnături digitale este o schemă dedicată denumită simplu Algoritmul de Semnătură Digitală (DSA). Similar într-o oarecare măsură cu protocolul Diffie-Hellman, DSA utilizează proprietățile algebrice ale exponențialului modular și ale inverselor multiplicative pentru generarea și verificarea semnăturilor.
Algoritmul de semnătură digitală pe curbe eliptice (ECDSA) este o variantă ECC a DSA, oferind aceeași funcționalitate dar cu chei semnificativ mai scurte. Aceasta duce la o eficiență îmbunătățită, făcându-l o alegere populară pentru sistemele cu constrângeri de resurse.
Atât DSA, cât și ECDSA vor fi ilustrate în mai mult detaliu mai târziu.
Scheme de semnătură digitală folosind criptosisteme generice
Pe lângă algoritmii dedicați, semnăturile digitale pot fi generate și folosind criptosisteme asimetrice generice, cum ar fi RSA.
RSA, care va fi discutat în detaliu într-o secțiune ulterioară, exploatează și el inversele multiplicative modulare și exponențierea modulară ca operații fundamentale, dar le combină într-o secvență diferită față de DSA. În RSA, semnatarul creează de obicei un hash al mesajului și apoi criptează hash-ul cu cheia sa privată, creând semnătura digitală. Orice parte poate verifica ulterior această semnătură decriptând-o cu cheia publică a semnatarului și comparând-o cu mesajul hash.
Aplicații ale criptografiei cu cheie asimetrică
Criptografia cu cheie asimetrică este ubiquitară în aplicațiile moderne de tehnologie digitală. Funcționalitățile de bază ale AKC descrise mai sus formează blocurile de construcție ale multor protocoale de aplicații de nivel superior, inclusiv:
-
Comunicarea pe internet: Comunicarea securizată pe internet, cum ar fi HTTPS, se bazează în mare măsură pe criptografia cu cheie asimetrică. Transport Layer Security (TLS) și predecesorul său, Secure Sockets Layer (SSL), utilizează criptografia cu cheie asimetrică în timpul procesului inițial de handshake pentru a stabili o cheie simetrică, care este apoi folosită pentru restul sesiunii de comunicare.
-
Autentificare: Criptografia cu cheie asimetrică este utilizată pentru a crea semnături digitale, permițând unei entități să autentifice un document sau un mesaj digital ca provenind de la un anumit expeditor. Aceasta este folosită în multe scenarii, de la verificarea actualizărilor de software până la contracte digitale cu forță juridică.
-
Criptarea e-mailurilor: Protocoalele de criptare a e-mailurilor precum PGP (Pretty Good Privacy) și alternativa sa open-source GPG (GNU Privacy Guard) utilizează criptografia cu cheie asimetrică pentru a se asigura că numai destinatarul vizat poate citi conținutul e-mailului.
-
Shell securizat (SSH): SSH este un protocol pentru conectare la distanță securizată și alte servicii de rețea securizate peste o rețea nesecurizată. Utilizează criptografia cu cheie asimetrică pentru a autentifica serverul față de client și, opțional, clientul față de server.
-
VPN (rețea privată virtuală): Criptarea cu cheie asimetrică este utilizată pentru a stabili conexiuni securizate în VPN-uri, asigurând comunicarea securizată prin rețele publice.
-
Blockchain și criptomonede: Tehnologiile blockchain, inclusiv Bitcoin și Ethereum, utilizează criptografia cu cheie asimetrică. De exemplu, proprietatea asupra Bitcoin este stabilită prin semnături digitale folosind criptografia cu cheie asimetrică.
-
Autorități de certificare: Criptografia cu cheie asimetrică este utilizată de autoritățile de certificare (CA) pentru a emite și semna certificate digitale, care sunt folosite în comunicarea TLS, semnarea codului, criptarea e-mailurilor și altele. Un certificat digital leagă o cheie publică de o entitate specifică (de exemplu, o persoană sau un server).

Figura 5. Emiterea și semnarea certificatelor digitale folosind criptografia cu cheie asimetrică
Securitatea criptografiei cu cheie asimetrică
Mai multe noțiuni criptografice se reunesc pentru a permite o criptografie cu cheie asimetrică securizată, inclusiv:
Secretul cheii private: Cea mai de bază cerință de securitate a AKC este că cheia privată rămâne secretă. Cu toate acestea, deoarece cheia privată trebuie să fie legată matematic de cheia publică, secretul cheii private necesită și că este imposibil din punct de vedere computațional să se derive cheia privată din cunoașterea cheii publice. Schemele de generare a cheilor în AKC se bazează pe probleme matematice dificile din punct de vedere computațional pentru a facilita această cerință.
Funcționalitate trapă: În AKC, operațiunile de criptare și decriptare implică chei complementare diferite dintr-o singură pereche de chei. Textul cifrat generat prin criptare folosind una dintre chei (de exemplu cheia publică) trebuie să fie de nedescifrat pentru terțe părți, în timp ce să fie ușor de decriptat de deținătorul cheii complementare (în acest caz cheia privată). Cu alte cuvinte, criptarea ar trebui să semene cu o funcție unidirecțională cu trapă, astfel încât terțele părți să nu poată inversa operația și recupera textul clar, dar cheia privată să ofere o trapă secretă care permite inversarea ușoară. Algoritmii AKC populari utilizează exponențierea modulară pentru a crea comportamentul de funcție unidirecțională cu trapă.
Aleatorietatea: Procesul de generare a cheilor ar trebui să exploateze și aleatorietatea pentru a se asigura că cheile sunt impredictibile, deoarece orice model sau predictibilitate în generarea cheilor ar putea fi exploatat de un atacator. Aleatorietatea este utilizată și pentru padding în timpul criptării pentru a genera texte cifrate semantic securizate și în schemele de semnătură digitală pentru a produce semnături unice chiar și atunci când același mesaj este semnat de mai multe ori. Din acest motiv, utilizarea generatoarelor de numere aleatoare puternice este o parte importantă a AKC.
Dimensiune mare a cheii: Ca și în cazul SKC, dimensiunile mari ale cheilor asigură protecție împotriva atacurilor prin forță brută. Cu toate acestea, deoarece dimensiunile mari ale cheilor cresc și costul computațional al procesului de criptare și decriptare, o soluție optimă trebuie să echilibreze considerațiile de securitate și eficiență. Tabelul următor prezintă dimensiunile tipice ale cheilor pentru diverse protocoale și aplicații de criptografie cu cheie asimetrică:
| Protocol | Dimensiuni tipice ale cheilor (în biți) | Aplicație |
|---|---|---|
| RSA | 1024 (depreciat), 2048, 3072, 4096 | Criptare, semnături digitale |
| DSA | 1024 (depreciat), 2048, 3072 | Semnături digitale |
| DH | 2048, 3072, 4096 | Schimb de chei |
| ECDH | 224, 256, 384, 521 | Schimb de chei |
| ECDSA | 224, 256, 384, 521 | Semnături digitale |
Infrastructura cu cheie publică: În AKC, cheile private trebuie păstrate secrete de proprietari, în timp ce cheile publice sunt partajate. Este nevoie de un mecanism securizat pentru a gestiona și distribui aceste chei publice între utilizatori. Infrastructura cu cheie publică (PKI) oferă o modalitate de a face acest lucru folosind certificate digitale. Un certificat digital oferă dovada identității proprietarului cheii publice și este emis de o autoritate de încredere, cum ar fi o autoritate de certificare (care face parte dintr-un PKI). Prin urmare, PKI joacă un rol integral în securitatea aplicațiilor moderne care utilizează AKC, permițând gestionarea cheilor la scară largă (prin crearea, gestionarea, distribuirea și revocarea certificatelor digitale).
Riscuri de securitate pentru criptografia cu cheie asimetrică
Așa cum se arată în tabelul de mai sus, algoritmii moderni cu cheie asimetrică, cum ar fi RSA, utilizează de obicei dimensiuni de cheie mult mai mari decât omologii cu cheie simetrică utilizați în mod obișnuit, cum ar fi AES-128. Chiar și protocoalele bazate pe ECC (ECDH și ECDSA) care au dimensiuni mai mici ale cheilor utilizează un minim de cel puțin 224 biți pentru chei. Aceasta implică la rândul său că un atac prin forță brută care implică o căutare exhaustivă a spațiului cheilor private pentru a identifica cheia corectă este intractabil din punct de vedere computațional în viitorul previzibil. Acest lucru rămâne valabil chiar dacă calculatoarele cuantice ar fi folosite pentru această sarcină. Prin urmare, atacurile împotriva AKC se concentrează de obicei pe exploatarea altor potențiale slăbiciuni ale criptosistemelor specifice. Unele moduri de atac bine documentate vizează:
-
Slăbiciunea algoritmică prin utilizarea mijloacelor matematice și computaționale sofisticate pentru a submina ipotezele de dificultate folosite pentru a formula algoritmii cu cheie asimetrică. De exemplu, securitatea RSA este condiționată de dificultatea factorizării numerelor prime mari, iar progresele computaționale recente au permis factorizarea cu succes a cheilor RSA de 829 de biți. Prin urmare, RSA pe 1024 biți este în prezent depreciat. Așa cum va fi discutat mai târziu, riscul principal pe care calculatoarele cuantice îl reprezintă pentru AKC se încadrează și în această categorie.
-
Aleatorietatea imperfectă, care poate duce la slăbiciuni în procesul de generare a cheilor. De exemplu, dacă generatorul de numere aleatoare utilizat pentru a crea cheile este defect și generează chei care nu sunt cu adevărat aleatoare, un atacator ar putea fi capabil să prevadă cheile.
-
Defecte de implementare, cum ar fi erori în implementarea algoritmilor criptografici care dezvăluie involuntar informații despre chei. De exemplu, padding-ul incorect poate dezvălui potențial detalii despre chei.
-
Canale laterale care se referă la scurgeri de informații din sistemul fizic care efectuează criptografia. Astfel de scurgeri ar putea fi sub formă de informații de sincronizare, consum de energie sau chiar sunet, care pot fi exploatate într-un așa-numit atac pe canal lateral. De exemplu, analizarea duratei de execuție a operațiunilor criptografice ar putea dezvălui numărul de „1"-uri dintr-o cheie binară. Această scurgere aparent inocentă restrânge semnificativ spațiul de căutare, dezvăluind soluții potențiale la probleme care inițial păreau de nerezolvat.
-
Schimbul de chei prin interceptarea cheilor în timp ce sunt schimbate, cum ar fi într-un atac de tip om-în-mijloc (MITM). Protocolul DH este susceptibil la atacuri MITM dacă nu sunt incorporate pași suplimentari de autentificare.
-
Stocarea cheilor prin vizarea furtului cheilor din spații de stocare insuficient securizate. Aceasta include atacuri fizice, cum ar fi manipularea sau furtul dispozitivelor de stocare.
Securizarea criptosistemelor cu cheie asimetrică împotriva varietății de atacuri posibile este, prin urmare, o sarcină semnificativă care implică considerații matematice, hardware, software, logistice și juridice.
RSA
Algoritmul RSA (Rivest-Shamir-Adleman) este unul dintre primele sisteme de criptografie cu cheie publică și este utilizat pe scară largă pentru transmiterea securizată a datelor. Este un sistem criptografic versatil, oferind operațiile necesare pentru a permite criptarea, decriptarea, semnăturile digitale și schimbul de chei într-un cadru comun.
În această secțiune, vom ilustra criptografia cu cheie asimetrică (AKC) folosind RSA prin exemple simple.
Vom folosi scenariul standard cu două părți, Alice și Bob, care doresc să comunice în siguranță folosind AKC.
Algoritmul RSA
Algoritmul RSA de bază implică patru operații: generarea cheilor, distribuirea cheilor, criptarea și decriptarea:
-
Generarea cheilor:
Cheile publice și private sunt generate pe baza principiilor matematice legate de numerele prime, unde calculul acestora este ușor, dar inversul este dificil.
Le vom numi astfel:
- Cheie publică:
- Cheie privată:
Observă că este comun atât cheii publice, cât și cheii private, și este cunoscut sub numele de modul. Vom avea nevoie de acesta mai târziu.
Detaliu matematic
- Alege două numere prime distincte, și .
- alese aleatoriu (pentru securitate).
- Ar trebui să fie de magnitudini similare, dar să difere ca număr de cifre, pentru a face factorizarea mai dificilă.
- Numerele prime pot fi alese eficient folosind un test de primalitate.
- Calculează .
- este modulul atât pentru cheile publice, cât și pentru cele private.
- Calculează totientul .
- Totientul trebuie păstrat secret și este de obicei eliminat după generarea cheilor.
- Alege un întreg astfel încât și .
- adică, și ar trebui să fie coprime.
- Acest număr formează exponentul cheii publice și este de obicei ales ca un număr mic pentru eficiență computațională.
- Numărul prim este folosit frecvent.
- Calculează pentru a satisface relația de congruență .
- Adică, este inversul multiplicativ al lui modulo .
- Acesta este calculat mai eficient folosind algoritmul euclidian extins.
- Acest număr este exponentul cheii private.
- Cheia publică este formată din , iar cheia privată este .
-
Distribuirea cheilor:
- Cheia publică este făcută publică celor care pot dori să trimită un mesaj
- Cheia privată este păstrată secretă.
-
Criptarea:
- Alice dorește să trimită un mesaj lui Bob. În acest caz un simplu număr întreg
- Alice folosește cheia publică a lui Bob pentru a cripta mesajul în textul cifrat
Detaliu matematic
- este un număr întreg .
- , unde este textul cifrat.
-
Decriptarea:
- Bob primește textul cifrat
- Bob folosește cheia sa privată pentru a decripta mesajul înapoi în mesajul
Detaliu matematic
- .
Acesta este conturul de bază al RSA. În practică, scheme de umplutură mai sofisticate sunt aplicate textului simplu înainte de criptare pentru a se asigura că texte simple egale conduc la texte cifrate diferite. Aceasta previne o serie de atacuri posibile împotriva implementărilor naive ale RSA și permite securitatea semantică.
Ilustrarea RSA în Python
În celulele de cod de mai jos, ilustrăm un exemplu simplu al algoritmului RSA folosind numere întregi mici și apoi demonstrăm distribuția practică a cheilor și aplicații de semnătură digitală folosind biblioteci Python care implementează RSA.
Notă: În această secțiune vom arăta calculele matematice în detaliu ca parte a codului Python
Generarea cheilor RSA
Să parcurgem pas cu pas o instanță simplă a algoritmului RSA folosind numere prime mici.
Va trebui să putem calcula cel mai mare divizor comun a două numere întregi, deoarece va fi necesar pentru a testa dacă două numere întregi sunt coprime.
Vom explica o metodă simplă de calcul a acestuia, dar este mult mai eficient cu numere întregi mai mari să folosești funcția Python math.gcd.
# Added by doQumentation — required packages for this notebook
!pip install -q cryptography
import math
# Example function to compute the gcd (greatest common divisor)
def gcd(a, b):
if b == 0:
return a
else:
return gcd(b, a % b)
# let's calculate some examples using algorithm
n1 = gcd(50, 10)
n2 = gcd(99, 33)
n3 = gcd(59, 9)
# do the same with the python library call
m1 = math.gcd(50, 10)
m2 = math.gcd(99, 33)
m3 = math.gcd(59, 9)
# Confirm they are the same
assert n1 == m1
assert n2 == m2
assert n3 == m3
# They are - print out the values for explanation
print("gcd(50,10) =", m1)
print("gcd(99,33) =", m2)
print("gcd(59,9) =", m3)
Prima fază a fluxului de lucru RSA este generarea cheilor. Aceasta este inițiată prin alegerea a două numere prime, care trebuie păstrate secrete de entitatea care generează cheile.
# Choosing two prime numbers and keep them secret
p = 13
q = 19
print("The secret prime numbers p and q are:", p, q)
Apoi, modulul , care este pur și simplu produsul celor două numere prime alese, este calculat. Acest modul va fi publicat ca parte a cheii publice.
# Calculate n which is the modulus for both the public and private keys
n = p * q
print("modulus n (p*q)=", n)
Funcția totient a lui Euler este calculată în continuare, deoarece este necesară pentru operația de invers multiplicativ modular folosită pentru a determina cheile în RSA. este de asemenea păstrat secret și de obicei eliminat după generarea cheilor.
# Compute Euler's totient function, φ(n) and keep it secret
phi = (p - 1) * (q - 1)
print("The secret Euler's function (totient) [phi(n)]:", phi)
Suntem acum gata să calculăm cheile publice și private. În RSA, fiecare dintre acestea este specificată printr-un tuplu de două numere întregi. Prima intrare din fiecare tuplu este un număr întreg distinct, iar a doua intrare este modulul care este comun ambelor chei.
Prima intrare din cheia publică poate fi orice număr întreg mai mare de 1 care este coprim cu . Două numere întregi sunt coprime dacă cel mai mare divizor comun al lor este 1. Deci folosim funcția math.gcd pentru a găsi un număr întreg coprim cu .
# Choose an integer e such that e and φ(n) are coprime
e = 2
while e < phi:
if math.gcd(e, phi) == 1:
break
else:
e += 1
print("Public Key (e):", e)
Cheia privată necesită un număr întreg , care este inversul multiplicativ al lui modulo ; adică, satisface relația de congruență . Pentru această ilustrare simplă în care lucrăm cu numere întregi mici, putem pur și simplu itera peste numerele întregi pozitive pentru a găsi un potrivit. În scenarii reale, algoritmul euclidian extins, eficient din punct de vedere computațional, este folosit în acest scop.
# Compute a value for d such that (d * e) % φ(n) = 1
d = 1
while True:
if (d * e) % phi == 1:
break
else:
d += 1
print("Private Key (d):", d)
Formăm acum tuplurile , care formează cheile publică și privată, respectiv. Cheia publică este apoi publicată, în timp ce cheia privată este păstrată secretă.
# Public and Private Key pair
public = (e, n)
private = (d, n)
print(f"The Public key is {public} and Private Key is {private}")
Criptarea și decriptarea în RSA folosesc operația de exponențiere modulară. Mai mult, cheile publice și private sunt complementare în sensul că oricare poate fi utilizată pentru a cripta un mesaj pe care cealaltă îl poate decripta.
Ilustrăm aici cazul în care cheia publică este utilizată pentru criptare și cheia privată este utilizată pentru decriptare, definind o funcție Python pentru fiecare operație.
Criptăm și decriptăm apoi un mesaj întreg .
# Encryption function
def encrypt(plain_text):
return (plain_text**e) % n
# Decryption function
def decrypt(cipher_text):
return (cipher_text**d) % n
# Simple message to encode
msg = 9
# encrypt then decrypt
enc_msg = encrypt(msg)
dec_msg = decrypt(enc_msg)
print("Original Message:", msg)
print("Encrypted Message:", enc_msg)
print("Decrypted Message:", dec_msg)
În timp ce numerele întregi mici utilizate mai sus sunt utile pentru a contura cu ușurință ideile de bază ale algoritmului RSA, în aplicații reale RSA necesită utilizarea unor numere întregi foarte mari. De exemplu, RSA pe 2048 de biți implică utilizarea unui modul de 2048 de biți, ale cărui echivalente în zecimal sunt în jur de 10. Aceste numere cu adevărat enorme sunt necesare pentru securitatea practică a RSA.
Schimbul de chei simetrice cu RSA
Așa cum am discutat anterior, AKC permite două părți care doresc să comunice să stabilească în siguranță un secret comun, care poate fi utilizat, de exemplu, ca cheie secretă pentru criptarea simetrică ulterioară a textului simplu în vrac.
Să luăm în considerare următorul scenariu. Alice și Bob vor să folosească SKC pentru a cripta și decripta mesaje. Cu toate acestea, înainte ca acest proces să poată fi inițializat, trebuie să convină asupra unei chei secrete comune. O opțiune este ca una dintre părți — să zicem, Alice — să genereze o cheie secretă și apoi să o transmită în siguranță lui Bob. Pentru a realiza acest transfer securizat, Alice decide să folosească RSA ca mecanism de încapsulare a cheilor (KEM).
Aceasta implică următorii pași:
- Mai întâi, Alice generează o cheie simetrică aleatorie, pe care intenționează să o împartă cu Bob.
- Apoi, Bob generează o pereche de chei asimetrice și face disponibilă cheia sa publică pe un canal adecvat.
- Apoi, Alice folosește cheia publică a lui Bob pentru a cripta cheia simetrică, încapsulând-o astfel într-un text cifrat.
- Apoi, Alice transmite textul cifrat pe un canal fiabil, dar nu neapărat securizat.
- În cele din urmă, Bob primește textul cifrat și îl decriptează folosind cheia sa privată. Acum are acces la cheia simetrică generată de Alice.

Figura 1. Schimbul de chei simetrice cu RSA
Exemplu de schimb de chei bazat pe umplutură în Python
Un flux de lucru practic care utilizează RSA pentru schimbul de chei non-interactiv bazat pe umplutură este ilustrat acum folosind biblioteca Python cryptography.
Importă modulele necesare din biblioteca Python cryptography. Dacă este nevoie, această bibliotecă poate fi instalată folosind comanda pip install cryptography.
Alice generează apoi o cheie secretă aleatorie, pe care intenționează să o transmită lui Bob.
# pip install cryptography
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
symmetric_key = Fernet.generate_key()
print(f"\nSymmetric key generated by Alice: {symmetric_key}")
Folosind modulul rsa din biblioteca cryptography, Bob generează o pereche de chei și apoi transmite cheia sa publică. Oricine poate intercepta cheia publică și poate citi numerele publice care formează cheia.
# Bob generates a 2048-bit RSA key pair
bob_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
bob_public_key = bob_private_key.public_key()
print(f"Public key broadcast by Bob: {bob_public_key}")
print(f"\nPublic numbers in Bobs' public key: {bob_public_key.public_numbers()}")
În acest moment, presupunem că Alice a primit cheia publică transmisă de Bob. symmetric_key-ul lui Alice de mai sus poate fi acum criptat folosind cheia publică a lui Bob pentru a produce textul cifrat. Într-un scenariu realist, Alice va folosi și metode suplimentare de umplutură cum ar fi OAEP pentru a asigura securitatea semantică a comunicărilor sale cu Bob.
# Encryption
ciphertext = bob_public_key.encrypt(
symmetric_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Ciphertext:", ciphertext)
Alice transmite apoi textul cifrat pe un canal deschis, încrezătoare că numai Bob cu cheia privată corespunzătoare va putea să îl decripteze. Presupunem că Bob a primit textul cifrat și îl poate acum decripta folosind cheia sa privată confidențială.
# Bob decrypts ciphertext to access the symmetric key
decrypted_symmetric_key = bob_private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Decrypted key:", decrypted_symmetric_key)
assert decrypted_symmetric_key == symmetric_key
În acest moment, atât Alice cât și Bob au acces la cheia simetrică secretă, pe care o pot folosi pentru aplicații SKC.
Simularea unui Mecanism de Încapsulare a Cheilor cu RSA în Python
În fluxul de lucru următor, ilustrăm utilizarea RSA pentru a simula un Mecanism de Încapsulare a Cheilor (KEM) prin care un mesaj secret aleatoriu suficient de lung este schimbat în siguranță și convertit ulterior într-un secret comun de lungime adecvată folosind un KDF.
Din nou, Alice și Bob vor să stabilească un secret comun în mod non-interactiv, iar Alice este cea care decide ce secret să folosească.
Începem prin importarea unor biblioteci Python necesare.
Bob generează apoi perechea sa de chei RSA și transmite cheia sa publică.
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from os import urandom
# Bob's RSA key pair
private_key_Bob = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key_Bob = private_key_Bob.public_key()
print("Bob's private and public keys created")
Alice generează mai întâi un secret aleatoriu lung din care secretul comun va fi eventual derivat. Într-un KEM pur, secretul lung va fi un element aleatoriu din structura algebrică ce stă la baza sistemului criptografic. În cazul RSA pe 2048 de biți, acesta ar fi un număr întreg aleatoriu modulo modulul RSA pe 2048 de biți. Deoarece un KEM pur nu necesită umplutură suplimentară, dar în acest exemplu simulăm doar un KEM folosind RSA și biblioteca cryptography necesită utilizarea umpluturilor la criptarea cu RSA. Deci vom folosi un secret lung oarecum mai scurt care este totuși mult mai lung decât o cheie AES standard de 256 de biți.
Alice_long_secret = urandom(160) # A 160 byte or 1280 bit random message
print("Alice's secret created")
Alice criptează secretul lung folosind cheia publică a lui Bob și secretul criptat este comunicat lui Bob.
Alice_encrypted_secret = public_key_Bob.encrypt(
Alice_long_secret,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Alice's secret encrypted")
Bob decriptează secretul criptat primit de la Alice folosind cheia sa privată.
Bob_decrypted_secret = private_key_Bob.decrypt(
Alice_encrypted_secret,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
assert Alice_long_secret == Bob_decrypted_secret, "Secrets do not match!"
# if we get here they match
print("Secrets match")
În cele din urmă, atât Alice cât și Bob aplică separat o Funcție de Derivare a Cheilor (KDF) convenită pe secretul lung pentru a deriva cheia simetrică.
Rețineți că procesul implică un protocol de hashing și utilizarea unui salt aleatoriu care asigură unicitatea și impredictibilitatea cheii simetrice comune în cazul în care secretul lung este vreodată reutilizat (nu este recomandat). Cu toate acestea, salt-ul în sine nu trebuie să fie secret și odată ce este generat aleatoriu, de exemplu de Alice în acest exemplu, poate fi transmis lui Bob alături de secretul lung criptat.
Vom presupune, prin urmare, că atât Alice cât și Bob au acces la același salt aleatoriu.
def key_derivation_function(secret, salt):
hkdf = HKDF(
algorithm=hashes.SHA256(),
length=32, # Desired key length
salt=salt,
info=None,
backend=None,
)
return hkdf.derive(secret)
common_salt = urandom(16) # Random salt accessible to both Alice and Bob
symmetric_key_Alice = key_derivation_function(Alice_long_secret, common_salt)
symmetric_key_Bob = key_derivation_function(Bob_decrypted_secret, common_salt)
assert symmetric_key_Alice == symmetric_key_Bob, "Derived keys do not match!"
print(
f"A symmetric key of length {len(symmetric_key_Alice)*8} bits was successfully derived by both Alice and Bob!"
)
Semnături digitale cu RSA
Vom extinde acum scenariul de comunicare confidențială de mai sus cu Alice și Bob la unul care include și validarea cu ajutorul unei semnături digitale.
Ca înainte, Alice va trimite confidențial un mesaj care încapsulează o cheie simetrică lui Bob, dar va semna digital și mesajul astfel încât Bob, la primirea mesajului, să poată verifica că Alice l-a origina și că conținutul mesajului nu a fost alterat în timpul transmiterii.
Mai general, este de dorit să se permită validarea fără a compromite confidențialitatea, prin care orice parte interesată poate verifica integritatea, autenticitatea și poate stabili non-repudierea cu privire la o anumită comunicare, chiar dacă acea parte nu are acces la mesajul text simplu propriu-zis.
Vom considera acest cadru general care implică următorii pași:
- Mai întâi, atât Bob cât și Alice fac disponibile cheile lor publice pe un canal deschis.
- Apoi, Alice criptează textul simplu folosind cheia publică a lui Bob, creând un text cifrat.
- Apoi, Alice creează un hash al textului cifrat cu o funcție hash și criptează ulterior hash-ul textului cifrat rezultat folosind cheia sa privată. Acest hash criptat este semnătura.
- Apoi, Alice transmite atât textul cifrat cât și semnătura pe un canal deschis.
- Apoi, Bob folosește cheia publică a lui Alice pentru a decripta semnătura, dezvăluind hash-ul textului cifrat.
- Apoi, deoarece Bob are și el acces la textul cifrat în sine, folosește aceeași funcție hash folosită de Alice pentru a recrea o a doua instanță a hash-ului textului cifrat. Dacă aceasta se potrivește cu cea obținută prin decriptarea semnăturii lui Alice, atunci mesajul este validat, chiar dacă textul cifrat în sine nu a fost încă decriptat.
- În cele din urmă, Bob, după ce a validat mesajul, decriptează textul cifrat folosind propria sa cheie privată.

Figura 2. Semnături digitale cu RSA
Acest flux de lucru pentru o semnătură digitală este ilustrat în continuare.
Importăm din nou câteva module utile din biblioteca cryptography.
Ca înainte, Alice intenționează să trimită în siguranță o cheie simetrică lui Bob, dar dorește și să o semneze digital. În acest caz, avem nevoie de chei publice atât pentru Alice cât și pentru Bob. Prin urmare, primul pas este ca atât Alice cât și Bob să creeze propria lor pereche de chei folosind RSA și să transmită propria lor cheie publică lumii întregi.
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
# Generate keys for Bob
bob_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
bob_public_key = bob_private_key.public_key()
# Generate keys for Alice
alice_private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
alice_public_key = alice_private_key.public_key()
print("Private and Public keys generated for Bob and Alice.")
În pasul următor, ca înainte, Alice folosește cheia publică a lui Bob pentru a cripta cheia simetrică și pregătește textul cifrat.
# Alice encrypts the message using Bob's public key
ciphertext = bob_public_key.encrypt(
symmetric_key,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("ciphertext of symmetric key: ", ciphertext)
În acest stadiu, în loc să transmită doar textul cifrat, Alice intenționează să atașeze o semnătură digitală la acesta pentru a-i putea dovedi lui Bob că ea a fost expeditoarea mesajului. Aceasta se face în două etape:
- Creează un hash al textului cifrat folosind un algoritm de hashing.
- Criptează hash-ul folosind cheia privată a lui Alice, ceea ce echivalează cu o semnătură.
# Alice signs the ciphertext using her private key
digest = hashes.Hash(hashes.SHA256())
digest.update(ciphertext)
hash_to_sign = digest.finalize()
signature = alice_private_key.sign(
hash_to_sign,
padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
Prehashed(hashes.SHA256()),
)
print("signature: ", signature)
Alice transmite apoi textul cifrat și semnătura pe o rețea astfel încât Bob să le poată intercepta pe amândouă.
# Bob receives the ciphertext and signature
received_ciphertext = ciphertext
received_signature = signature
# Send signature and ciphertext here
print("Sending ciphertext and signature.....")
Pe partea lui Bob, prima sarcină este să verifice integritatea și autenticitatea textului cifrat. Pentru a face acest lucru, Bob creează un hash al textului cifrat primit folosind același algoritm de hashing folosit de Alice.
# Bob creates a hash of the ciphertext using the same algorithm used by Alice
digest = hashes.Hash(hashes.SHA256())
digest.update(received_ciphertext)
hash_to_verify = digest.finalize()
print("hash to verify: ", hash_to_verify)
Apoi, Bob decriptează semnătura primită folosind cheia publică a lui Alice. Deoarece Alice a folosit cheia sa privată pentru a crea semnătura, Bob poate să o decripteze folosind cheia publică a lui Alice. Semnătura decriptată nu este altceva decât un hash al textului cifrat creat de Alice. Dacă hash-ul creat de Bob se potrivește cu semnătura decriptată, atunci Bob a verificat că textul cifrat pe care l-a primit nu a fost alterat și că Alice a semnat textul cifrat.
În codul Python de mai jos, aceste operații sunt combinate într-o funcție utilă numită verify furnizată de un obiect asociat cu cheia publică a lui Alice.
from cryptography.exceptions import InvalidSignature
def is_signature_valid(public_key, signature, data_hash):
try:
public_key.verify(
signature,
data_hash,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH
),
Prehashed(hashes.SHA256()),
)
return True
except InvalidSignature:
return False
if is_signature_valid(alice_public_key, received_signature, hash_to_verify):
print("The signature is valid.")
else:
print("The signature is not valid.")
După ce a verificat integritatea și autenticitatea textului cifrat primit, Bob poate să îl decripteze folosind cheia sa privată, deoarece Alice a creat textul cifrat folosind cheia publică a lui Bob.
# Bob decrypts the message using his private key
decrypted_message = bob_private_key.decrypt(
received_ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)
print("Decrypted message:", decrypted_message.decode())
În scenariul de semnătură digitală de mai sus, orice parte — nu doar Bob — poate verifica că Alice este expeditoarea textului cifrat deoarece toată lumea poate accesa cheia publică a lui Alice, textul cifrat și semnătura digitală. Mai mult, după trimiterea textului cifrat și a semnăturii, Alice nu poate nega ulterior că a făcut-o deoarece semnătura poate fi decriptată la un hash semnificativ folosind doar cheia sa publică. Aceasta stabilește non-repudierea.
Prin permiterea distribuirii securizate a cheilor și susținerea non-repudierii, criptografia cu cheie publică stabilește o bază solidă pentru comunicarea digitală securizată.
Spargerea RSA
Utilitatea și securitatea algoritmului RSA descris mai sus se bazează pe două ipoteze matematice:
-
Găsirea inversului multiplicativ modular cunoscând doar este imposibilă din punct de vedere computațional.
-
În contextul RSA, operația de exponențiere modulară se comportă ca o funcție unidirecțională cu trapă. Când este folosită pentru criptare, produce un text cifrat de neînțeles, iar fără acces la cheia privată, inversarea operației pentru a recupera textul clar din textul cifrat nu este fezabilă. Totuși, cu acces la cheia privată, care acționează ca o trapă, textul cifrat poate fi decriptat cu ușurință.
Cel mai proeminent atac asupra algoritmului RSA urmărește să submineze ipoteza 1, recuperând eficient numărul cheii private prin factorizarea modulului . Așa cum va fi ilustrat mai jos, este ușor să recuperezi dacă ai acces la factorii primi și ai lui sau la totientul . Reamintește-ți că , și sunt ținute secrete în timpul generării cheii și eliminate după aceea. O terță parte care recuperează aceste informații folosind un calculator clasic sau cuantic descoperă practic cheia privată, spărgând RSA. Astfel, factorizarea în numere prime este operația computațională esențială necesară pentru spargerea RSA.
Calculul clasic și RSA
Factorizarea în numere prime a numerelor întregi mari prezintă o scalare superpolinomială sau subexponențială pe calculatoarele clasice. Cel mai cunoscut algoritm clasic pentru factorizarea numerelor întregi mai mari de este sita generală a câmpului de numere (GNFS)
Detalii matematice
ca funcție a numărului întreg de factorizat.
Această scalare este superpolinomială în numărul de biți necesari pentru a reprezenta .
Prin urmare, factorizarea în numere prime este considerată ineficientă pe calculatoarele clasice.
În prezent, cele mai mari numere întregi factorizate pe hardware clasic sunt în jur de 829 de biți sau 250 de cifre zecimale. Având în vedere creșterea exponențială a puterii de calcul clasice observată în ultimele câteva decenii, RSA pe 1024 de biți nu mai este considerat sigur pe termen scurt și este acum depreciat. Cu toate acestea, în viitorul previzibil, factorizarea numerelor întregi de 2048 de biți cu magnitudinea în jur de este considerată în prezent nefezabilă pe sistemele clasice, sugerând o viabilitate continuă. Apariția calculatoarelor cuantice, totuși, anulează această evaluare.
Algoritmul cuantic al lui Shor și RSA
Probabil cel mai cunoscut algoritm cuantic de astăzi este algoritmul lui Shor pentru găsirea factorilor primi ai numerelor întregi. Când a fost introdus de Peter Shor în 1994, a fost recunoscut ca primul algoritm cuantic care oferea o accelerare superpolinomială față de algoritmii clasici pe o problemă de mare importanță practică, și anume factorizarea în numere prime.
Algoritmul lui Shor poate factoriza numere prime cu unde este numărul de biți.
Explicație matematică a algoritmului lui Shor
În contextul RSA, algoritmul lui Shor funcționează exploatând periodicitatea funcției exponențiale modulare și oferă o primitivă cuantică de găsire a perioadei care permite factorizarea eficientă în numere prime a modulului .
O schiță simplificată la nivel înalt a schemei generale a lui Shor pentru spargerea RSA este următoarea:
-
Dat modulul , care este publicat ca parte a cheii publice, alege un număr coprim cu , adică . Deoarece știm că are exact doi factori primi , aproape orice număr mai mic decât pe care îl alegem aleatoriu este probabil coprim cu .
-
Având ales , găsește exponentul astfel încât . Aceasta implică . Existența unui exponent astfel încât congruența de mai sus să fie satisfăcută este garantată de proprietatea de periodicitate a exponențierii modulare.
-
Dacă este par,