2015-07-20 71 views
2

由於mcrypt被認爲已經過時,我的任務是升級當前代碼以使用openssl。聽起來很簡單,但...經過幾天的嘗試和失敗,我覺得瘋了。使用openssl解密mcrypt

我對你的問題是:你有沒有辦法用以前用mcrypt加密過的openssl數據解密?我已經閱讀了很多關於這個問題的文章,其中大多數人都說在運行mcrypt之前,必須先手動填充數據。 問題是已加密的mcrypt-ed數據(使用mcrypt提供的自動填充空值)並駐留在數據庫中,因此對其進行修改是不可能的和/或期望的。

提及:

  1. 所使用的算法是Rijndael的-128 CBC與一個32字節的密鑰(所以我使用AES-256-CBC加入OpenSSL)。
  2. 我爲php(php-crypto)使用openssl包裝。
  3. 我已經設法通過簡單地剝離結束解碼字符(如果它們不是字母數字)來使逆操作起作用(使用mcrypt解碼openssl)。
  4. 在mcrypting之前手動填充數據,然後使用openssl解密它就像一個魅力,但這不是問題。

一些代碼片段:

// Simple mcrypt encrypt, decrypt with php-crypto example 
// This doesn't work and produces a "Finalizing of cipher failed" error 
     $data = "This is a text"; 
     $strMcryptData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv); 

     $algorithm = 'aes-256-cbc'; 
     $cipher = new Cipher($algorithm); 
     $sim_text = $cipher->decrypt($strMcryptData, $key, $iv); 

// Simple mcrypt encrypt with padding, decrypt with php-crypto 
// Works and produces the correct text on decryption 
     $pad = $blocksize - (strlen($data) % $blocksize); 
     $text = $data; 
     $text .= str_repeat(chr($pad), $pad); 
     $strPaddedData=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv); 

     $sim_text = $cipher->decrypt($strPaddedData, $key, $iv); 
+2

是的,它應該是可能的。您可能需要提供您的代碼才能幫助您。不要忘記添加示例輸入,輸出和預期輸出。如果你不想,那麼我不知道你在找什麼樣的答案。 –

+0

另請參閱[將我的加密庫從Mcrypt升級到OpenSSL](http://stackoverflow.com/q/43329513/608639)和[準備在PHP 7.2中刪除Mcrypt](http://stackoverflow.com/q/ 42696657/608639) – jww

回答

4

如果您在mcrypt中進行加密而無需手動添加PKCS7,mcrypt會很樂意將您的明文填充爲NUL字節。

只要使用aes-X-cbc,OpenSSL將爲您做PKCS7填充。不幸的後果是,如果你有AES-CBC(NULL_PADDED(plaintext))並試圖解密它,openssl_decrypt將嘗試刪除填充並失敗。

比較http://3v4l.org/bdQe9 VS http://3v4l.org/jr68fhttp://3v4l.org/K6ZEU

OpenSSL的擴展目前不提供您一個方式說「此字符串不填充,請不要剝奪填充爲我」,然後取下NUL字節你自己。您必須使用PKCS7填充進行加密才能使解密成功。

儘管這是OpenSSL的一個限制,但它強調的是,你遇到它的唯一原因是因爲mcrypt is terrible

2

不應該有任何重大分歧除了的填充。如果您直接使用更高級別的OpenSSL(EVP)構造,則應該能夠調用EVP_CIPHER_CTX_set_padding。我認爲填充參數應該爲零,儘管it is not documented。您需要一個預配置的加密/解密上下文。

然後你會得到與密文長度相同的明文。最後零到十五個字節將被設置爲零。您需要手動刪除這些字節。如果明文恰好以零字節結束,那麼這些也將被刪除;但如果明文是可打印字符串(使用8位編碼),則永遠不會如此。您可能希望確保不會刪除多於15個字節。

如果你得到完全隨機的明文,那麼你的密鑰或密文是不正確的。如果你得到可讀的明文,但是前16個字節,那麼你的IV處理是不正確的。

+0

雖然我不確定這個方法是否可以通過PHP包裝器API訪問,但您可能需要調用本機代碼。 –

+0

不幸的是,你必須重新實現你的mcrypt加密函數來強制PKCS7填充openssl來成功解密它。 –

+0

@ScottArciszewski填充選項是否仍未實現?它們出現在API ...中:' 選項可以是OPENSSL_RAW_DATA,OPENSSL_ZERO_PADDING' –

3

有點老,但你可以通過一些工作來解決這個問題。您可以可以告訴PHP的OpenSSL加密的字符串沒有填充,並告訴它給你原始輸出(所以你不必base64解碼它)。然後,如果字符串的長度恰好可以被IV完全整除,則可以從結果字符串的末尾去除空值(這是一種完整性檢查,就像生成的字符串不能被IV整除然後它不是根本填充)。

請注意,這段代碼有兩個主要的侷限性

  1. 如果在任何時候,你加密的,在兩個或兩個以上NULL字節結束合法的字符串,則該代碼將不會給你相同的輸出。

  2. 如果字符串的填充只需要一個空字節,那麼這段代碼不會去掉它。

可以解決這兩個如果你知道一個事實,你沒有加密任何在空字節結束,你可以改變,只是做了preg_replace這條空值的代碼;只要確保將正則表達式錨定到字符串的末尾,以便從尾部剝離即可。

http://3v4l.org/kYAXn

顯然,這種代碼與沒有大的免責聲明,並請測試它在你的使用情況,但有人可能希望發現這很有用。

+0

完美適用於PHP5.6,PHP7和PHP7.1。非常感謝您的工作! –