2008-08-14 11 views
39

avevo bisogno di qualche semplice crittografia stringa, quindi ho scritto il seguente codice (con una grande quantità di "ispirazione" da here):Perché una password errata causa "Padding non valido e non può essere rimosso"?

// create and initialize a crypto algorithm 
    private static SymmetricAlgorithm getAlgorithm(string password) { 
     SymmetricAlgorithm algorithm = Rijndael.Create(); 
     Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
      password, new byte[] { 
      0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,    // salty goodness 
      0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65 
     } 
     ); 
     algorithm.Padding = PaddingMode.ISO10126; 
     algorithm.Key = rdb.GetBytes(32); 
     algorithm.IV = rdb.GetBytes(16); 
     return algorithm; 
    } 

    /* 
    * encryptString 
    * provides simple encryption of a string, with a given password 
    */ 
    public static string encryptString(string clearText, string password) { 
     SymmetricAlgorithm algorithm = getAlgorithm(password); 
     byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText); 
     MemoryStream ms = new MemoryStream(); 
     CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write); 
     cs.Write(clearBytes, 0, clearBytes.Length); 
     cs.Close(); 
     return Convert.ToBase64String(ms.ToArray()); 
    } 

    /* 
    * decryptString 
    * provides simple decryption of a string, with a given password 
    */ 
    public static string decryptString(string cipherText, string password) { 
     SymmetricAlgorithm algorithm = getAlgorithm(password); 
     byte[] cipherBytes = Convert.FromBase64String(cipherText); 
     MemoryStream ms = new MemoryStream(); 
     CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write); 
     cs.Write(cipherBytes, 0, cipherBytes.Length); 
     cs.Close();    
     return System.Text.Encoding.Unicode.GetString(ms.ToArray()); 
    } 

Il codice sembra funzionare bene, tranne che quando decrittografia dei dati con un errato chiave, ottengo una CryptographicException - "Padding non è valido e non può essere rimosso" - sulla riga cs.Close() in decryptString.

codice di esempio:

string password1 = "password"; 
    string password2 = "letmein"; 
    string startClearText = "The quick brown fox jumps over the lazy dog"; 
    string cipherText = encryptString(startClearText, password1); 
    string endClearText = decryptString(cipherText, password2);  // exception thrown 

viene, è questo che ci si aspetta mia domanda? Avrei pensato che decifrare con una password sbagliata avrebbe comportato un output senza senso, piuttosto che un'eccezione.

+5

Questo mi ha risparmiato così tanto tempo con il tuo commento: "Il codice sembra funzionare bene, tranne che quando decrittando i dati con un tasto errato" "I _swore_ Avevo copiato i tasti, ma guardando 2x non l'ho fatto.Speriamo che questo aiuti qualcun altro prima di osservare il meccanismo di riempimento o di modificare il codice. – atconway

risposta

26

Anche se questo è stato già risposto penso che sarebbe una buona idea per spiegare perché è previsto.

Uno schema di riempimento viene in genere applicato perché la maggior parte dei filtri crittografici non è semanticamente sicura e per impedire alcune forme di criptoatack. Ad esempio, in genere in RSA viene utilizzato lo schema di riempimento OAEP che impedisce alcuni tipi di attacchi (come un attacco di testo normale scelto o blinding).

Uno schema di riempimento aggiunge alcuni elementi (solitamente) casuali al messaggio m prima che il messaggio venga inviato. Nel metodo OAEP, per esempio, due Oracoli sono utilizzati (questa è una spiegazione semplicistica):

  1. date le dimensioni del modulo vi DiPAD bit k1 con 0 e k0 bit con un numero casuale.
  2. Quindi applicando alcune trasformazioni al messaggio si ottiene il messaggio inserito che viene crittografato e inviato.

Ciò fornisce una randomizzazione per i messaggi e un modo per verificare se il messaggio è spazzatura o meno. Dato che lo schema di padding è reversibile, quando decifri il messaggio mentre non puoi dire nulla sull'integrità del messaggio stesso puoi, in effetti, fare qualche asserzione sul padding e quindi puoi sapere se il messaggio è stato correttamente decrittografato o stai facendo qualcosa di sbagliato (cioè qualcuno ha manomesso il messaggio o stai usando la chiave sbagliata)

+0

Jorge, grazie per la spiegazione. Sto vedendo lo stesso comportamento descritto, i dati decrittografati sono corretti. dovrei mangiare questa eccezione, o (si spera) c'è qualcosa che sto facendo in modo errato che posso correggere? Cosa non va quando viene lanciata l'eccezione? tutti i post che ho letto sembrano scritti da persone che sono più interessate a far sparire l'eccezione. nel mio caso voglio che il mio utilizzo sia corretto :) – stuck

+1

Questo non risponde alla domanda dell'OP, la domanda riguarda un codice a blocchi simmetrico Rijndael, non un codice asimmetrico come RSA. Per un codice a blocchi viene aggiunto il riempimento per rendere i dati da crittografare un multiplo della dimensione del blocco, generalmente utilizzando PKCS # 7 (née PKCS # 5). Con questi schemi di riempimento i byte casuali sono ** non ** aggiunti. Si noti che il padding ISO 10126 è stato ritirato e che i byte casuali non hanno aggiunto alcuna sicurezza. La risposta sarebbe al punto se parlava di riempimento simmetrico della crittografia. Suggerimento: correggi la risposta per rispondere alla domanda. ZOMG, Senior Developer. – zaph

+1

@zaph Sto solo spiegando genericamente perché si ottiene un'eccezione di riempimento non valida invece di dati inutili quando si decripta l'utilizzo e la password non valida. RSA era solo un esempio, lo schema di riempimento specifico utilizzato era ed esempio. Risponde all'opinione perché dice: Sì, è previsto ed ecco perché e la spiegazione di quello strano riferimento "padding" che puoi capire solo se capisci cos'è uno schema di padding e perché è usato, ma ti permetterebbe sconcertato se non lo fai (cosa ha a che fare la crittografia con il padding ???) –

3

Sì, questo è da aspettarsi, o per lo meno, la sua esattamente ciò che accade quando la nostra routine di crittografia ottenere dati non decifrabile

1

Ci possono essere alcuni byte non letti nel CryptoStream. Chiudere prima di leggere completamente il flusso stava causando l'errore nel mio programma.

5

Se si desidera che l'utilizzo sia corretto, è necessario aggiungere authentication al testo cifrato in modo che sia possibile verificare che sia la pasword corretta o che il testo cifrato non sia stato modificato. Il padding che si sta utilizzando ISO10126 genera un'eccezione solo se l'ultimo byte non decrittografa come uno dei 16 valori validi per il riempimento (0x01-0x10). Quindi hai una probabilità 1/16 di NON gettare l'eccezione con la password sbagliata, dove se l'autentichi hai un modo deterministico per capire se la decrittografia è valida.

Utilizzando le API di crittografia mentre apparentemente facile, in realtà è piuttosto facile commettere errori.Ad esempio, si utilizza un valore fisso per la chiave e la derivazione iv, ovvero ogni testo cifrato crittografato con la stessa password riutilizzerà la sua IV con quella chiave, che infrange la sicurezza semantica con la modalità CBC, l'IV deve essere sia imprevedibile che unico per una determinata chiave.

Per questo motivo di facile fare errori, ho un frammento di codice, che cerco di tenere rivisti e aggiornati (commenti, numeri Benvenuti):

Modern Examples of Symmetric Authenticated Encryption of a string C#.

Se si utilizza è AESThenHMAC.AesSimpleDecryptWithPassword(ciphertext, password) quando viene utilizzata la password errata, viene restituito null, se il testo cifrato o iv è stato modificato dopo la codifica, viene restituito null, non si otterranno mai dati indesiderati o un'eccezione di riempimento.

16

Ho riscontrato un "Riempimento" non valido e non può essere rimosso. " eccezione, ma nel mio caso la chiave IV e il padding erano corretti.

Si è scoperto che lo svuotamento del flusso crittografico è tutto ciò che mancava.

Ti piace questa:

  MemoryStream msr3 = new MemoryStream(); 
      CryptoStream encStream = new CryptoStream(msr3, RijndaelAlg.CreateEncryptor(), CryptoStreamMode.Write); 
      encStream.Write(bar2, 0, bar2.Length); 
      // unless we flush the stream we would get "Padding is invalid and cannot be removed." exception when decoding 
      encStream.FlushFinalBlock(); 
      byte[] bar3 = msr3.ToArray(); 
+1

Lo stesso dovrebbe fare 'encStream.Close();'. – sharpener

+1

Forse dovresti usare 'using' su quegli oggetti dato che sono usa e getta. Sarebbe abbastanza – user2173353

+1

Si noti inoltre che un semplice 'Flush()' che si farebbe normalmente con un flusso * non * uguale al 'FlushFinalBlock()' richiesto per un 'CryptoStream'. Questo è leggermente ambiguo nella descrizione che precede il codice. –

0

Ho avuto un problema simile, la questione di metodo decifrare è stato in fase di inizializzazione di un flusso di memoria vuota. quando funzionava quando ho inizializzato con l'array di byte di testo cifrato come questo:

MemoryStream ms = new MemoryStream(cipherText) 
-1

La risposta aggiornato dall'utente "atconway" lavorato per me.

Il problema non riguardava il riempimento ma la chiave che era diversa durante la crittografia e la decrittografia. La chiave e iv dovrebbero essere le stesse durante l'encoding e la decodifica dello stesso valore.

-1

Stavo anche ricevendo il Padding non valido e non può essere rimosso il messaggio. Come detto sopra, la causa erano alcuni byte bufferizzati nel CryptoStream. Questo è quanto è stato fissato dal chiamando il metodo FlushFinalBlock():

using (CryptoStream cryptoStream = new CryptoStream(memoryStream, algorithm.CreateDecryptor(), CryptoStreamMode.Write)) { 
    cryptoStream.Write(bytes, 0, bytes.Length); 
    cryptoStream.FlushFinalBlock(); 
    result = Encoding.UTF8.GetString(memoryStream.ToArray()); 
    return result; 
} 
2

Un altro motivo dell'eccezione potrebbe essere una condizione di competizione tra diversi thread che utilizzano la logica decrittazione - implementazioni native di ICryptoTransform sono non thread-safe (ad esempio SymmetricAlgorithm), quindi dovrebbe essere messo in una sezione esclusiva, ad es utilizzando il blocco . prega di fare riferimento qui per maggiori dettagli: http://www.make-awesome.com/2011/07/system-security-cryptography-and-thread-safety/

3

Se hai escluso chiave mancata corrispondenza, quindi oltre FlushFinalBlock() (si veda la risposta di Yaniv), chiamando Close() sulla CryptoStream anche bastare.

Se state pulendo le risorse strettamente con using blocchi, assicurarsi di nido il blocco per il CryptoStream sé:

using (MemoryStream ms = new MemoryStream()) 
using (var enc = RijndaelAlg.CreateEncryptor()) 
{ 
    using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write)) 
    { 
    encStream.Write(bar2, 0, bar2.Length); 
    } // implicit close 
    byte[] encArray = ms.ToArray(); 
} 

Sono stato morso da questo (o simile):

using (MemoryStream ms = new MemoryStream()) 
using (var enc = RijndaelAlg.CreateEncryptor()) 
using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write)) 
{ 
    encStream.Write(bar2, 0, bar2.Length); 
    byte[] encArray = ms.ToArray(); 
} // implicit close -- too late! 
+1

Ma se si esegue 'encStream.FlushFinalBlock()' Suppongo che non importi dove si esegue 'ms.ToArray()' (sia all'esterno che all'interno di 'CryptoStream''s' using'). – nawfal

+0

L'OP sa che si tratta di una mancata corrispondenza della chiave che ha causato l'errore e ha chiesto perché una mancata corrispondenza della chiave causa questo errore. – jbtule