Decifrare un dato cifrato significa recuperare il testo in chiaro a partire dal ciphertext, usando la stessa chiave (o la chiave corrispondente) con cui è stato cifrato. Sembra l'operazione speculare della cifratura, ma in pratica servono tre informazioni corrette insieme: l'algoritmo, la chiave e i parametri della modalità, come IV, nonce o tag di autenticazione. Manca anche solo uno di questi e la decifratura fallisce, o in certi casi produce un output corrotto senza nemmeno segnalare l'errore.

Le richieste di "come decifrare" un file o un messaggio arrivano quasi sempre da uno di questi scenari: un backup cifrato di cui hai perso la password ma non la chiave derivata, un'analisi forense su un dispositivo sotto mandato, o una challenge CTF. Nei due articoli precedenti abbiamo visto i concetti base della crittografia e come funzionano simmetrica, asimmetrica e hashing. Qui vediamo il lato opposto: come si decifra in pratica, e dove finisce la decrittazione legittima e inizia la crittanalisi.

Decrittazione o crittanalisi: dove passa il confine

Il confine tra le due cose è la chiave. Se la possiedi e la usi per invertire la cifratura, stai decifrando. Se non la possiedi e provi a ricostruirla o ad aggirarla partendo dal ciphertext, quella è crittanalisi: brute force su chiavi deboli, attacchi a testo noto (known-plaintext), attacchi a testo cifrato scelto (chosen-ciphertext) o side-channel come il padding oracle contro CBC.

La distinzione conta perché cambia completamente l'approccio tecnico. Decifrare è un'operazione deterministica: chiave corretta, output corretto, in millisecondi. La crittanalisi è probabilistica e spesso computazionalmente costosa, e ha senso solo in contesti autorizzati come pentest, forensics con mandato o competizioni CTF.

Decifrare AES-CBC in Python

CBC (Cipher Block Chaining) richiede la chiave e l'IV (initialization vector) usati in fase di cifratura, oltre alla rimozione del padding PKCS7 applicato prima di cifrare.

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
import os

key = os.urandom(32)  # AES-256
iv = os.urandom(16)

# cifratura
padder = padding.PKCS7(128).padder()
padded_data = padder.update(b"messaggio segreto") + padder.finalize()

encryptor = Cipher(algorithms.AES(key), modes.CBC(iv)).encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()

# decifratura: servono chiave e IV, non basta la chiave
decryptor = Cipher(algorithms.AES(key), modes.CBC(iv)).decryptor()
padded_plaintext = decryptor.update(ciphertext) + decryptor.finalize()

unpadder = padding.PKCS7(128).unpadder()
plaintext = unpadder.update(padded_plaintext) + unpadder.finalize()

print(plaintext)  # b'messaggio segreto'

Un errore comune in decifratura CBC è un'eccezione di unpadding: significa che i byte di padding attesi alla fine del testo non tornano, quasi sempre perché la chiave o l'IV usati per decifrare non sono quelli giusti, oppure il ciphertext è stato alterato in transito. Questo stesso comportamento, se un'applicazione lo espone come messaggio di errore distinguibile dall'esterno, è la base del padding oracle attack: un attaccante può usare la differenza tra "padding valido" e "padding non valido" per decifrare un messaggio senza mai conoscere la chiave.

Decifrare AES-GCM: il ruolo del tag di autenticazione

GCM (Galois/Counter Mode) si comporta diversamente da CBC in un punto decisivo: se chiave, nonce o ciphertext non corrispondono esattamente, la decifratura non produce un output corrotto ma solleva un'eccezione esplicita.

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
import os

key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12)

ciphertext = aesgcm.encrypt(nonce, b"messaggio segreto", None)

try:
    plaintext = aesgcm.decrypt(nonce, ciphertext, None)
except InvalidTag:
    print("Chiave, nonce o ciphertext non corrispondono: decifratura impossibile")

Questa proprietà, chiamata autenticazione, è il motivo per cui GCM ha sostituito CBC come modalità di default: previene silenziosamente la manomissione del ciphertext, oltre a rendere molto più semplice il debug quando la decifratura fallisce.

Decifrare con RSA: la chiave privata in azione

RSA decifra solo con la chiave privata corrispondente alla chiave pubblica usata in cifratura. Nella pratica RSA non cifra dati in volume, come spiegato nell'articolo sulla crittografia asimmetrica: si usa per scambiare una chiave di sessione simmetrica, non per il contenuto vero e proprio.

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

oaep = padding.OAEP(
    mgf=padding.MGF1(algorithm=hashes.SHA256()),
    algorithm=hashes.SHA256(),
    label=None
)

ciphertext = public_key.encrypt(b"chiave di sessione AES", oaep)
plaintext = private_key.decrypt(ciphertext, oaep)

print(plaintext)  # b'chiave di sessione AES'

Le chiavi RSA usate qui sono concettualmente le stesse chiavi che generi per l'autenticazione SSH: una coppia pubblica/privata dove solo chi possiede la privata può decifrare (o firmare).

Quando la decrittazione diventa un attacco

Se la chiave non è disponibile, provare comunque a decifrare richiede una strategia diversa a seconda di come la chiave è stata generata:

  • chiave derivata da una password debole: un attacco a dizionario o brute force contro la funzione di derivazione (PBKDF2, Argon2) può ricostruire la chiave se la password è comune o corta, con tool come hashcat.
  • chiave a lunghezza insufficiente: chiavi AES a 64 o 128 bit generate con generatori pseudocasuali deboli sono attaccabili in tempi ragionevoli con hardware dedicato; con AES-256 e un generatore crittograficamente sicuro il brute force diretto sulla chiave resta computazionalmente non praticabile.
  • riuso di IV o nonce: in CBC un IV riutilizzato espone pattern nei blocchi cifrati; in GCM il riuso del nonce con la stessa chiave è più grave, perché permette di recuperare il tag di autenticazione e falsificare messaggi.
  • padding oracle: se un'applicazione distingue tra errore di padding e altri errori, un attaccante può decifrare un messaggio CBC byte per byte senza mai avere la chiave, interrogando ripetutamente l'oracolo.

Questi sono scenari da pentest o CTF, non operazioni di routine: richiedono autorizzazione esplicita quando applicati a sistemi non propri.

Quale scenario ti serve davvero

Scenario Hai la chiave? Approccio Tool tipici
Recupero di un backup cifrato personale Sì, ma persa la password Riderivare la chiave con la stessa KDF (PBKDF2/Argon2) usata in cifratura script Python, OpenSSL
Analisi forense con mandato Spesso no Estrazione della chiave da memoria o keystore del dispositivo Volatility, Autopsy
CTF o ricerca in ambiente controllato No, è l'obiettivo della sfida Crittanalisi: brute force, padding oracle, known-plaintext hashcat, John the Ripper, CyberChef
Debug di un'applicazione che cifra dati propri Decifratura diretta come negli esempi sopra script dedicato nello stesso linguaggio dell'app

Risorse