2011-09-06 9 views
17

Sto tentando di crittografare/decodificare una stringa utilizzando la crittografia AES a 128 bit (ECB). Quello che voglio sapere è come aggiungere/rimuovere il padding PKCS7 ad esso. Sembra che l'estensione Mcrypt possa occuparsi della crittografia/decrittografia, ma il padding deve essere aggiunto/rimosso manualmente.Come aggiungere/rimuovere il riempimento PKCS7 da una stringa crittografata AES?

Qualche idea?

+3

Solo una nota: Se si può cambiare, usare [un altro modo di BCE (è insicuro)] (http: //en.wikipedia. org/wiki/Block_cipher_modes_of_operation). –

+0

@Paul No, non può cambiare, è ciò che dipende dal sistema del client. Qualche possibilità che tu possa guidarmi con l'imbottitura? –

+0

Intendevo AES, risolto. –

risposta

46

Vediamo. PKCS # 7 è descritto in RFC 5652 (Sintassi dei messaggi crittografici).

Lo schema di riempimento è indicato nella sezione 6.3. Content-encryption Process. In pratica dice: aggiungi quel numero di byte necessario per riempire la dimensione del blocco data (ma almeno uno), e ognuno di essi dovrebbe avere la lunghezza del padding come valore.

Quindi, guardando l'ultimo byte decrittografato sappiamo quanti byte rimuovere. (Si potrebbe anche verificare che abbiano tutti lo stesso valore.)

Ora potrei darvi una coppia di funzioni PHP, ma il mio PHP è un po 'arrugginito. Quindi fatelo anche voi (quindi sentitevi liberi di modificare la mia risposta per aggiungerla), o date un'occhiata allo user-contributed notes alla documentazione di mcrypt - alcuni di questi sono sul padding e forniscono un'implementazione del padding PKCS # 7.


Quindi, diamo un'occhiata sul first note there in dettaglio:

<?php 

function encrypt($str, $key) 
{ 
    $block = mcrypt_get_block_size('des', 'ecb'); 

Questo ottiene la dimensione del blocco dell'algoritmo utilizzato. Nel tuo caso, useresti aes o rijndael_128 invece di des, suppongo (non l'ho provato). (Al contrario, si può semplicemente prendere 16 qui per AES, invece di invocare la funzione.)

 $pad = $block - (strlen($str) % $block); 

Questo calcola le dimensioni imbottitura. strlen($str) è la lunghezza dei dati (in byte), % $block fornisce il resto modulo $block, ovvero il numero di byte di dati nell'ultimo blocco. $block - ... fornisce quindi il numero di byte necessari per riempire quest'ultimo blocco (questo è ora un numero compreso tra 1 e $block, incluso).

 $str .= str_repeat(chr($pad), $pad); 

str_repeat produce una stringa costituita da una ripetizione della stessa stringa, qui una ripetizione del character given by$pad, $pad volte, cioè una stringa di lunghezza $pad, riempito con $pad. $str .= ... aggiunge questa stringa di riempimento ai dati originali.

 return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB); 

Ecco la crittografia stessa. Utilizzare MCRYPT_RIJNDAEL_128 anziché MCRYPT_DES.

} 

Ora l'altra direzione:

function decrypt($str, $key) 
{ 
    $str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB); 

La decrittazione. (Ovviamente cambierai l'algoritmo, come sopra). $ str ora è la stringa decrittografata, incluso il padding.

 $block = mcrypt_get_block_size('des', 'ecb'); 

Questa è di nuovo la dimensione del blocco. (Vedi sopra.)

 $pad = ord($str[($len = strlen($str)) - 1]); 

Questo sembra un po 'strano. Meglio scriverlo in più fasi:

$len = strlen($str); 
    $pad = ord($str[$len-1]); 

$len è ora la lunghezza della stringa imbottita, e $str[$len - 1] è l'ultimo carattere di questa stringa. ord lo converte in un numero. Quindi $pad è il numero che abbiamo usato in precedenza come valore di riempimento per il padding, e questa è la lunghezza del padding.

 return substr($str, 0, strlen($str) - $pad); 

Così ora abbiamo tagliato gli ultimi $pad byte dalla stringa. (Invece di strlen($str) potremmo anche scrivere $len qui: substr($str, 0, $len - $pad).).

} 

?> 

noti che invece di utilizzare substr($str, $len - $pad), si può anche scrivere substr($str, -$pad), come la funzione substr in PHP ha una speciale trattamento per operandi negativi/argomenti, a contare dalla fine della stringa. (Non so se questo è più o meno efficiente di ottenere la lunghezza prima e calcolare l'indice manualmente.)

Come detto prima e annotato nel commento di rossum, invece di spogliare semplicemente l'imbottitura come fatto qui, si dovrebbe verificare che sia corretto - vale a dire guardare substr($str, $len - $pad) e verificare che tutti i suoi byte siano chr($pad). Questo serve da controllo contro la corruzione (anche se questo controllo è più efficace se si utilizza una modalità di concatenamento anziché ECB e non è un sostituto per un MAC reale).


(E ancora, dire al vostro cliente che dovrebbero pensare di cambiare a una modalità più sicura di BCE.)

+0

Se potessi dare qualche esempio di codice per farlo in pseudo codice o java, sarebbe fantastico. Non riesco ancora a capire come fare questo, cioè qual è la dimensione del blocco dato, qual è l'ultimo byte decodificato, ecc. –

+0

La dimensione del blocco per AES è 128 bit, cioè 16 byte. Avrai bisogno di un modo per sapere che il tuo messaggio è finito, naturalmente ... ma quale è il problema semplicemente prendendo la fonte data in una delle note alla documentazione di PHP? Il mio pseudo-codice non sarebbe molto meglio. –

+0

Solo che il codice sul manuale php non è troppo ben documentato, in realtà non capisco cosa stia facendo. Se potessi pubblicare un po 'di Java con commenti e alcuni possibili, sarebbe davvero utile. –

6

Ho creato due metodi per eseguire l'imbottitura e unpadding. Le funzioni sono documentate usando phpdoc e richiedono PHP 5. Come si noterà la funzione unpad contiene molta gestione delle eccezioni, generando non meno di 4 messaggi diversi per ogni possibile errore.

Per ottenere la dimensione del blocco per mcrypt PHP, è possibile utilizzare mcrypt_get_block_size, che definisce anche la dimensione del blocco in byte anziché bit.

/** 
* Right-pads the data string with 1 to n bytes according to PKCS#7, 
* where n is the block size. 
* The size of the result is x times n, where x is at least 1. 
* 
* The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3. 
* This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES. 
* 
* @param string $plaintext the plaintext encoded as a string containing bytes 
* @param integer $blocksize the block size of the cipher in bytes 
* @return string the padded plaintext 
*/ 
function pkcs7pad($plaintext, $blocksize) 
{ 
    $padsize = $blocksize - (strlen($plaintext) % $blocksize); 
    return $plaintext . str_repeat(chr($padsize), $padsize); 
} 

/** 
* Validates and unpads the padded plaintext according to PKCS#7. 
* The resulting plaintext will be 1 to n bytes smaller depending on the amount of padding, 
* where n is the block size. 
* 
* The user is required to make sure that plaintext and padding oracles do not apply, 
* for instance by providing integrity and authenticity to the IV and ciphertext using a HMAC. 
* 
* Note that errors during uppadding may occur if the integrity of the ciphertext 
* is not validated or if the key is incorrect. A wrong key, IV or ciphertext may all 
* lead to errors within this method. 
* 
* The version of PKCS#7 padding used is the one defined in RFC 5652 chapter 6.3. 
* This padding is identical to PKCS#5 padding for 8 byte block ciphers such as DES. 
* 
* @param string padded the padded plaintext encoded as a string containing bytes 
* @param integer $blocksize the block size of the cipher in bytes 
* @return string the unpadded plaintext 
* @throws Exception if the unpadding failed 
*/ 
function pkcs7unpad($padded, $blocksize) 
{ 
    $l = strlen($padded); 

    if ($l % $blocksize != 0) 
    { 
     throw new Exception("Padded plaintext cannot be divided by the block size"); 
    } 

    $padsize = ord($padded[$l - 1]); 

    if ($padsize === 0) 
    { 
     throw new Exception("Zero padding found instead of PKCS#7 padding"); 
    }  

    if ($padsize > $blocksize) 
    { 
     throw new Exception("Incorrect amount of PKCS#7 padding for blocksize"); 
    } 

    // check the correctness of the padding bytes by counting the occurance 
    $padding = substr($padded, -1 * $padsize); 
    if (substr_count($padding, chr($padsize)) != $padsize) 
    { 
     throw new Exception("Invalid PKCS#7 padding encountered"); 
    } 

    return substr($padded, 0, $l - $padsize); 
} 

Ciò non inficia la risposta di Paolo Ebermann in qualsiasi modo, è fondamentalmente la stessa risposta nel codice & phpdoc anziché come descrizione.


Si noti che la restituzione di un errore di imbottitura per un utente malintenzionato potrebbe risultare in un imbottitura attacco oracolo che rompe completamente CBC (quando CBC viene usato al posto di BCE o un cifrario sicuro e autenticato).

0

basta chiamare la seguente funzione dopo aver decrittografare i dati

function removePadding($decryptedText){ 
    $strPad = ord($decryptedText[strlen($decryptedText)-1]); 
    $decryptedText= substr($decryptedText, 0, -$strPad); 
    return $decryptedText; 
} 
+0

Il codice di risposta non funzionerà con padding nullo su cui PHP mcrypt è impostato di default. Per il padding PKCS # 7/PKCS # 5 è necessario verificare che il padding sia valido. Considera l'uso della chiave sbagliata, $ strPad molto probabilmente sarebbe sbagliato, potenzialmente un valore maggiore della lunghezza dei dati. Ma non restituire un errore di riempimento errato, che tende a creare un oracle di riempimento, invece non fare nulla. La maggior parte delle librerie supporta il padding PKCS # 7 e aggiungerà automaticamente il padding sulla crittografia e rimuoverà il padding in caso di decrittografia: non è necessario fare altro. – zaph

Problemi correlati