Ich versuche, eine Zeichenfolge mit 128-Bit-AES-Verschlüsselung (ECB) zu verschlüsseln/entschlüsseln. Was ich wissen möchte, ist, wie ich die PKCS7-Auffüllung hinzufügen/entfernen kann. Es scheint, dass die Mcrypt-Erweiterung die Verschlüsselung/Entschlüsselung übernehmen kann, aber die Auffüllung muss manuell hinzugefügt/entfernt werden.
Irgendwelche Ideen?
Mal sehen. PKCS #7 ist in RFC 5652 (Cryptographic Message Syntax) beschrieben.
Das Auffüllschema selbst ist in Abschnitt angegeben 6.3. Inhaltsverschlüsselungsprozess. Es besagt im Wesentlichen: Hängen Sie so viele Bytes an, wie Sie benötigen, um die angegebene Blockgröße (aber mindestens eines) zu füllen, und jeder von ihnen sollte die Auffülllänge als Wert haben.
Wenn wir uns also das letzte entschlüsselte Byte ansehen, wissen wir, wie viele Bytes entfernt werden müssen. (Man könnte auch überprüfen, ob sie alle den gleichen Wert haben.)
Ich könnte Ihnen jetzt ein paar PHP-Funktionen dafür geben, aber mein PHP ist ein bisschen eingerostet. Machen Sie dies entweder selbst (dann können Sie meine Antwort bearbeiten, um sie hinzuzufügen), oder sehen Sie sich die an von Benutzern beigesteuerte Notizen zur mcrypt-Dokumentation – einige von ihnen handeln vom Padding und stellen eine Implementierung von PKCS #7 Padding bereit.
Schauen wir uns also die an erste Notiz dort im Detail:
<?php
function encrypt($str, $key)
{
$block = mcrypt_get_block_size('des', 'ecb');
Dies erhält die Blockgröße des verwendeten Algorithmus. In Ihrem Fall würden Sie verwenden aes
oder rijndael_128
anstatt des
, nehme ich an (ich habe es nicht getestet). (Stattdessen könntest du einfach nehmen 16
hier für AES, anstatt die Funktion aufzurufen.)
$pad = $block - (strlen($str) % $block);
Dadurch wird die Polsterungsgröße berechnet. strlen($str)
ist die Länge Ihrer Daten (in Bytes), % $block
ergibt den Rest modulo $block
dh die Anzahl der Datenbytes im letzten Block. $block - ...
gibt also die Anzahl der Bytes an, die benötigt werden, um diesen letzten Block zu füllen (dies ist jetzt eine Zahl zwischen 1
und $block
inklusive).
$str .= str_repeat(chr($pad), $pad);
str_repeat
erzeugt eine Zeichenfolge, die aus einer Wiederholung derselben Zeichenfolge besteht, hier eine Wiederholung der Zeichen gegeben durch $pad
, $pad
Zeiten, dh eine Zeichenfolge der Länge $pad
gefüllt mit $pad
.
$str .= ...
fügt diese Füllzeichenfolge an die ursprünglichen Daten an.
return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
Hier ist die Verschlüsselung selbst. Verwenden MCRYPT_RIJNDAEL_128
anstatt MCRYPT_DES
.
}
Jetzt die andere Richtung:
function decrypt($str, $key)
{
$str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
Die Entschlüsselung. (Sie würden natürlich den Algorithmus wie oben ändern). $str ist jetzt die entschlüsselte Zeichenfolge, einschließlich der Auffüllung.
$block = mcrypt_get_block_size('des', 'ecb');
Dies ist wieder die Blockgröße. (Siehe oben.)
$pad = ord($str[($len = strlen($str)) - 1]);
Das sieht etwas seltsam aus. Schreiben Sie es besser in mehreren Schritten:
$len = strlen($str);
$pad = ord($str[$len-1]);
$len
ist jetzt die Länge der aufgefüllten Zeichenfolge und $str[$len - 1]
ist das letzte Zeichen dieser Zeichenfolge. ord
wandelt diese in eine Zahl um. Daher $pad
ist die Zahl, die wir zuvor als Füllwert für die Polsterung verwendet haben, und dies ist die Polsterungslänge.
return substr($str, 0, strlen($str) - $pad);
Also schneiden wir jetzt den letzten ab $pad
Bytes aus der Zeichenfolge. (Anstatt strlen($str)
wir könnten auch schreiben $len
Hier: substr($str, 0, $len - $pad)
.).
}
?>
Beachten Sie, dass anstatt zu verwenden substr($str, $len - $pad)
kann man auch schreiben substr($str, -$pad)
als die substr
Funktion in PHP hat eine spezielle Handhabung für negative Operanden/Argumente, um vom Ende des Strings zu zählen. (Ich weiß nicht, ob dies mehr oder weniger effizient ist, als zuerst die Länge zu ermitteln und den Index manuell zu berechnen.)
Wie bereits gesagt und im Kommentar von rossum angemerkt, sollte man die Polsterung nicht wie hier einfach abziehen, sondern auf Korrektheit prüfen – also anschauen substr($str, $len - $pad)
und überprüfen Sie, ob alle seine Bytes sind chr($pad)
. Dies dient als leichte Überprüfung gegen Korruption (obwohl diese Überprüfung effektiver ist, wenn Sie einen Verkettungsmodus anstelle von ECB verwenden, und kein Ersatz für einen echten MAC ist).
(Und sagen Sie Ihrem Kunden trotzdem, dass er darüber nachdenken sollte, zu einem sichereren Modus als ECB zu wechseln.)
Ich habe zwei Methoden erstellt, um das Auffüllen und Auffüllen durchzuführen. Die Funktionen werden mit dokumentiert phpdoc
und erfordern PHP 5. Wie Sie feststellen werden, enthält die Unpad-Funktion eine Menge Ausnahmebehandlung und generiert nicht weniger als 4 verschiedene Meldungen für jeden möglichen Fehler.
Um die Blockgröße für PHP mcrypt zu ermitteln, können Sie verwenden mcrypt_get_block_size
die auch die Blockgröße in Bytes anstelle von Bits definiert.
/**
* 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);
}
Dies macht die Antwort von Paŭlo Ebermann in keiner Weise ungültig, es ist im Grunde dieselbe Antwort in Code & PHPDoc statt als Beschreibung.
Beachten Sie, dass die Rückgabe eines Padding-Fehlers an einen Angreifer zu a führen kann Polsterung Orakelangriff was CBC vollständig unterbricht (wenn CBC anstelle von ECB oder einer sicheren authentifizierten Chiffre verwendet wird).
Rufen Sie einfach die folgende Funktion auf, nachdem Sie die Daten entschlüsselt haben
function removePadding($decryptedText){
$strPad = ord($decryptedText[strlen($decryptedText)-1]);
$decryptedText= substr($decryptedText, 0, -$strPad);
return $decryptedText;
}
Nur eine Anmerkung: Wenn Sie ändern können, verwenden Sie ein anderer Modus als ECB (es ist unsicher).
– Paulo Ebermann
6. September 2011 um 18:23 Uhr
@Paul Nein, kann nicht geändert werden, davon hängt das System des Kunden ab. Könntest du mich vielleicht mit der Polsterung anleiten?
– Ali
6. September 2011 um 18:31 Uhr
Verwenden Sie nicht den ECB-Modus. Wenn Ihr Kunde denkt, dass er ECB braucht, liegt er falsch. Verwenden Sie CTR oder CBC, stellen Sie sicher, dass Sie Ihre Chiffretexte authentifizieren.
– Scott Arciszewski
29. Juli 2015 um 16:34 Uhr
Diese Antwort behandelt das Hinzufügen von Padding zu bereits verschlüsselten Daten: stackoverflow.com/questions/24404770/…
– rsc
3. Februar 2021 um 7:07 Uhr