執行 trim($decrypted) 將會移除解密後可能發生的 Null 填充。
問題是,如果您加密的內容像是 MSWord 文件,它通常會以 Null 結尾。結果 $decrypted 將會小於原始明文,因此在 MSOffice 中開啟會失敗。
為了解決這個問題,請確保儲存原始明文的長度,並且在解密時執行以下操作:
$decrypted = substr(mdecrypt_generic($td, $encrypted), 0, $originalLength);
(PHP 4 >= 4.0.2, PHP 5, PHP 7 < 7.2.0, PECL mcrypt >= 1.0.0)
mcrypt_module_open — 開啟要使用的演算法和模式的模組
此函式在 PHP 7.1.0 已被棄用,並在 PHP 7.2.0 中移除。強烈不建議依賴此函式。
$algorithm
,$algorithm_directory
,$mode
,$mode_directory
此函式開啟要使用的演算法和模式的模組。演算法的名稱在 algorithm 中指定,例如 "twofish"
,或是 MCRYPT_ciphername
常數之一。模組透過呼叫 mcrypt_module_close() 關閉。
algorithm
MCRYPT_ciphername
常數之一,或演算法名稱(字串)。
algorithm_directory
algorithm_directory
參數用於定位加密模組。當您提供目錄名稱時,將會使用它。當您將其設定為空字串(""
)時,將會使用 php.ini 指令 mcrypt.algorithms_dir
設定的值。如果未設定,則使用的預設目錄是編譯到 libmcrypt 中的目錄(通常是 /usr/local/lib/libmcrypt)。
mode
MCRYPT_MODE_modename
常數之一,或以下字串之一:"ecb"、"cbc"、"cfb"、"ofb"、"nofb" 或 "stream"。
mode_directory
mode_directory
參數用於定位加密模組。當您提供目錄名稱時,將會使用它。當您將其設定為空字串(""
)時,將會使用 php.ini 指令 mcrypt.modes_dir
設定的值。如果未設定,則使用的預設目錄是編譯到 libmcrypt 中的目錄(通常是 /usr/local/lib/libmcrypt)。
正常情況下,它會傳回一個加密描述符,如果發生錯誤,則傳回 false
。
範例 #1 mcrypt_module_open() 範例
<?php
$td = mcrypt_module_open(MCRYPT_DES, '',
MCRYPT_MODE_ECB, '/usr/lib/mcrypt-modes');
$td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
?>
上述範例的第一行將嘗試從預設目錄開啟 DES
密碼,並從 /usr/lib/mcrypt-modes 目錄開啟 ECB
模式。第二個範例使用字串作為密碼和模式的名稱,只有在擴充功能連結到 libmcrypt 2.4.x 或 2.5.x 時才有效。
範例 #2 在加密中使用 mcrypt_module_open()
<?php
/* 開啟密碼 */
$td = mcrypt_module_open('rijndael-256', '', 'ofb', '');
/* 建立 IV 並確定金鑰大小長度,在 Windows 上使用 MCRYPT_RAND
* 代替 */
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_DEV_RANDOM);
$ks = mcrypt_enc_get_key_size($td);
/* 建立金鑰(僅限範例:MD5 不是適合此用途的雜湊演算法) */
$key = substr(hash('md5', 'very secret key'), 0, $ks);
/* 初始化加密 */
mcrypt_generic_init($td, $key, $iv);
/* 加密資料 */
$encrypted = mcrypt_generic($td, 'This is very important data');
/* 終止加密處理常式 */
mcrypt_generic_deinit($td);
/* 初始化解密加密模組 */
mcrypt_generic_init($td, $key, $iv);
/* 解密加密字串 */
$decrypted = mdecrypt_generic($td, $encrypted);
/* 終止解密處理常式並關閉模組 */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
/* 顯示字串 */
echo trim($decrypted) . "\n";
?>
執行 trim($decrypted) 將會移除解密後可能發生的 Null 填充。
問題是,如果您加密的內容像是 MSWord 文件,它通常會以 Null 結尾。結果 $decrypted 將會小於原始明文,因此在 MSOffice 中開啟會失敗。
為了解決這個問題,請確保儲存原始明文的長度,並且在解密時執行以下操作:
$decrypted = substr(mdecrypt_generic($td, $encrypted), 0, $originalLength);
對於像這樣的錯誤:
' mcrypt_module_open(): 無法在 '' 中開啟加密模組 '
請確保您使用正確的名稱。提供密碼清單的頁面不是表示每個密碼的正確方式(此處顯示:https://php.dev.org.tw/manual/en/mcrypt.ciphers.php)。
為了查看支援哪些密碼,請嘗試 phpinfo(); 並在 mcrypt 下尋找類似這樣的內容:
mcrypt 支援 => 已啟用
mcrypt_filter 支援 => 已啟用
版本 => 2.5.8
Api 編號 => 20021217
支援的加密演算法 => cast-128 gost rijndael-128 twofish arcfour cast-256 loki97 rijndael-192 saferplus wake blowfish-compat des rijndael-256
serpent xtea blowfish enigma rc2 tripledes
支援的模式 => cbc cfb ctr ecb ncfb nofb ofb stream
還應指出,在為 mcrypt 建立金鑰時,不應使用 md5() 和/或 sha1()。這是因為十六進位編碼僅使用一組 16 個字元 [0-9a-f],相當於 4 位元,因此會使您的加密強度減半:4 x 32 = 128 位元。
我已重新編寫了顯示的範例,因此以下是我的建議,以獲得真正的 256 位元加密
<?php
$key1 = "this is a secret key";
$key2 = "this is the second secret key";
$input = "Let us meet at 9 o'clock at the secret place.";
$length = strlen($input);
/* 開啟加密演算法 */
$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
/* 建立初始化向量 (IV) 並確定金鑰大小長度,在 Windows 上使用 MCRYPT_RAND 取代 */
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
$ks = mcrypt_enc_get_key_size($td);
/* 建立金鑰 */
$key1 = md5($key1);
$key2 = md5($key2);
$key = substr($key1, 0, $ks/2) . substr(strtoupper($key2), (round(strlen($key2) / 2)), $ks/2);
$key = substr($key.$key1.$key2.strtoupper($key1),0,$ks);
/* 初始化加密 */
mcrypt_generic_init($td, $key, $iv);
/* 加密資料 */
$encrypted = mcrypt_generic($td, $input);
/* 終止加密處理 */
mcrypt_generic_deinit($td);
/* 初始化解密模組 */
mcrypt_generic_init($td, $key, $iv);
/* 解密加密的字串 */
$decrypted = mdecrypt_generic($td, $encrypted);
/* 終止解密處理並關閉模組 */
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
/* 顯示字串 */
echo "Text: ".substr($decrypted,0,$length) . "<br>";
echo "Encoded: ".$encrypted ."<br>";
echo "<br>key1: $key1 <br>key2: $key2<br>created key: $key";
?>
關於先前關於使用十六進位格式和大小寫來改進金鑰的評論
這似乎很明顯,但選擇僅限於十六進位字元 ([0-9a-z]);您可以從 md5() 或 sha1() 取得原始 RAW 輸出,而不是預設的可讀十六進位格式。
原始輸出的結果將是 16 或 20 個(取決於使用的雜湊函數)範圍在 0-255 的字元序列。比 [0-9a-z] 甚至 [0-9a-zA-Z] 好得多。
16 或 20 通常低於最大金鑰長度(範例中的 $ks),但您可以將兩個或多個金鑰附加在一起
<?php
$human_key1 = 'something very secret';
$human_key2 = 'something else very secret';
// 使用兩個「人類可讀」金鑰和 sha1 的 40 位元組二進位金鑰。
$bigger_binary_key = sha1($human_key1, true) . sha1($human_key2, true);
// 然後像您會做的那樣使用它(從範例中提取)
$key = substr($bigger_binary_key, 0, $ks);
?>
...或者您可以自動將一個大型「人類金鑰」分成兩個或多個部分,使用 sha1(原始輸出!)雜湊這些部分,然後將它們再次合併在一起(以原始順序或重新排列、加鹽、根據您的喜好轉換它們)以取得 40、60、80 或更多字元的二進位金鑰,具體取決於秘密金鑰已分割成的部分數目 =)
關於使用 Windows 的 mcrypt_module_open() 錯誤的匿名後續
<?php
$M = mcrypt_list_modes();
$A = mcrypt_list_algorithms();
foreach ($M as $m)
foreach ($A as $a) {
$t = @mcrypt_module_open($a,'',$m,'');
print "$m, $a = ";
print ($t)?"ok":"nope";
print "<br>";
}
?>
這將顯示並非所有模式都適用於所有演算法。Cygwin 也沒有 'libmcrypt.dll',它也只能使用某些組合。
(第一次嘗試恰好是其中一個不起作用的!)
請記住,mcrypt 函數不實作像 pkcs#5 之類的填充。這會導致結尾處出現零位元組的問題,並且無法在其他環境中正確解碼字串。
有關如何新增 pkcs 5 填充的範例,請參閱 ref.mcrypt.php
稍微改進 dinamic 建立金鑰的函數
我認為弱點是字串的同一部分始終使用大寫字母。以下程式碼會將字串的隨機字元大寫,使金鑰更難預測
<?php
$key = substr($key1, 0, $ks/2) . substr($key2, (round(strlen($key2) / 2)), $ks/2);
$key = substr($key.$key1.$key2.$key1,0,$ks);
$buffer = str_split($key);
$limit = count($buffer)-1;
srand((float)microtime() * 1000000);
$end = rand(0, $limit);
$a = 0;
// replace random chars with capitals
while ($a < $end) {
list($usec, $sec) = explode(' ', microtime());
$seed = ((float)$sec) + ((float) $usec * 100000);
mt_srand($seed);
$index = mt_rand(0,$limit);
$buffer[$index] = strtoupper($buffer[$index]);
$a++;
}
$key = join('', $buffer);
?>