2011-09-06 163 views
17

我試圖使用128位AES加密(ECB)加密/解密字符串。我想知道的是我可以如何添加/刪除PKCS7填充。似乎Mcrypt擴展可以處理加密/解密,但填充必須手動添加/刪除。如何從AES加密字符串添加/刪除PKCS7填充?

任何想法?

+3

只是一個音符後:如果你可以改變,使用[另一種模式比ECB(它是不安全的)](HTTP://en.wikipedia。組織/維基/ Block_cipher_modes_of_operation)。 –

+0

@Paul不,不能改變,它的客戶系統依賴於什麼。你有什麼可以指導我填充的東西嗎? –

+0

我的意思是AES,修正了它。 –

回答

46

讓我們來看看。在RFC 5652(加密消息語法)中描述了PKCS#7。

填充方案本身在6.3. Content-encryption Process部分給出。它基本上是這樣說的:根據需要添加多個字節以填充給定的塊大小(但至少有一個),並且它們中的每一個應具有填充長度作爲值。

因此,看着最後一個解密的字節,我們知道有多少個字節需要去掉。 (也可以檢查它們都具有相同的值。)

我現在可以給你一對PHP函數來做到這一點,但我的PHP有點生疏。所以要麼自己做(然後可以自由地編輯我的答案來添加它),或者查看mcrypt文檔的user-contributed notes - 其中一些是關於填充並提供PKCS#7填充的實現。


那麼,讓我們來看看它first note there詳細:

<?php 

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

這得到所使用的算法的塊大小。在你的情況下,你會使用aesrijndael_128而不是des,我想(我沒有測試它)。 (相反,你可以簡單地採取16這裏AES,而不是調用函數。)

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

此計算填充大小。 strlen($str)是您的數據長度(以字節爲單位),% $block給出的餘數爲$block,即最後一個數據塊的數據字節數。 $block - ...因此給出了填充該最後一個塊所需的字節數(現在這是一個介於1$block之間的數字)。

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

str_repeat產生由相同的字符串的重複,這裏所涉及的character given by$pad的重複,$pad倍,即長度$pad的字符串,填充有$pad的字符串。 $str .= ...將此填充字符串追加到原始數據。

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

這裏是加密本身。使用MCRYPT_RIJNDAEL_128而不是MCRYPT_DES

} 

現在另一個方向:

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

解密。 (你當然會改變算法,如上)。 $ str現在是解密的字符串,包括填充。

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

這又是塊大小。 (見上文)

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

這看起來有點奇怪。最好寫多個步驟:

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

$len現在填充的字符串的長度,而$str[$len - 1]是該字符串的最後一個字符。 ord將其轉換爲數字。因此,$pad是我們以前用作填充的填充值的數字,這是填充長度。

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

所以現在我們從字符串切斷最後$pad字節。 (而不是strlen($str)我們也可以在這裏寫$lensubstr($str, 0, $len - $pad)。)。

} 

?> 

注意,代替使用substr($str, $len - $pad),還可以寫substr($str, -$pad),如PHP的substr功能有特殊的處理負的操作數/參數,從字符串末尾數。 (我不知道這是否比首先獲取長度或者手動計算索引效率更高或更低)

如前所述,並且在評論中由rossum指出,而不是簡單地剝離填充像完成在這裏,你應該檢查它是否正確 - 即查看substr($str, $len - $pad),並檢查其所有字節是否爲chr($pad)。這是對腐敗的輕微檢查(儘管如果您使用鏈接模式而不是ECB,並且不是實際MAC的替代品,則此檢查更有效)。


(仍在,告訴你的客戶,他們應該考慮改變比歐洲央行更安全的模式。)

+0

如果你可以給一些代碼做僞代碼或java代碼,那會很棒。我仍然不太明白如何做到這一點,即給定的塊大小是多少,最後解碼的字節是什麼等等。 –

+0

AES的塊大小是128位,即16個字節。當然,您需要一些方法來了解您的消息已經結束......但是,如何簡單地將其中一個註釋中給出的源代碼提供給PHP文檔,會出現什麼問題?我的僞代碼不會好很多。 –

+0

只是在PHP手冊上的代碼沒有很好的記錄,我實際上並沒有明白它在做什麼。如果你可以用一些可能的評論發佈一些Java,那真的很有用。 –

6

我創建了兩種方法來進行填充和去填充。這些函數使用phpdoc進行了記錄,並且需要PHP 5.如您將注意到unpad函數包含大量異常處理,爲每個可能的錯誤生成不少於4個不同的消息。

要獲得PHP mcrypt的塊大小,您可以使用mcrypt_get_block_size,它也定義塊大小爲字節而不是位。

/** 
* 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); 
} 

這不以任何方式聖保羅Ebermann的答案無效,它基本上是在代碼& PHPDoc的,而不是作爲描述了同樣的回答。


注意,返回一個填充錯誤,攻擊者可能會導致填充預言攻擊從而徹底打破了CBC(CBC時使用的,而不是歐洲央行或安全認證的密碼)。

0

只需調用下面的函數,你對數據進行解密

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

答案代碼不適用於PHP mcrypt默認使用的空填充。對於PKCS#7/PKCS#5填充,需要檢查填充是否有效。考慮使用錯誤的鍵,$ strPad很可能是錯誤的,可能是一個大於數據長度的值。但是不要返回一個錯誤的填充錯誤,這往往會創建一個填充oracle,而不會執行任何操作。大多數庫支持PKCS#7填充,並將自動添加填充加密和刪除解密填充 - 沒有更多的需要完成。 – zaph