本文轉載了 資深的 PHP 開發人員 Octavia Andreea Anghel 在 DevX.com 發表的 PHP 加密技術的教學文件,第一部分介紹了 PHP 預設的加密功能,及擴充模組 MCrypt 的安裝及應用方法。本文將會繼續介紹其他擴充模組。
用 MHash 建立散列值
MHash 是一個免費的函式庫,提供大量散列值算法,這些算法可用來計算校驗值 (checksum)、訊息摘要 (message digests)、及建立數碼簽署。
安裝 libmhash
- 下載 libmhash.dll。
- 複製 libmhash.dll 到 {php_home}/ext 及 {Windows_home}/System32 資料夾。
- 在 php.ini 中把 extension=php_mhash.dll 前面的評論符號「;」刪除,啟動這個模組。
- 儲存更新後的 php.ini。
支援的散列算法
MHash 目前支援的散列算法包括:
- MHASH_ADLER32
- MHASH_CRC32
- MHASH_CRC32B
- MHASH_GOST
- MHASH_HAVAL128
- MHASH_HAVAL160
- MHASH_HAVAL192
- MHASH_HAVAL256
- MHASH_MD4
- MHASH_MD5
- MHASH_RIPEMD160
- MHASH_SHA1
- MHASH_SHA256
- MHASH_TIGER
- MHASH_TIGER128
- MHASH_TIGER160
以下範例把明文 textfile.txt 加密並把結果寫到 encrypted.txt:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | $file = 'textfile.txt'; |
| 03 | $initial_contents = file_get_contents($file); |
| 04 | |
| 05 | if ($initial_contents) { |
| 06 | // mhash() 使用 MHASH_MD5 算法來處理 $initial_contents |
| 07 | $encrypted = mhash(MHASH_MD5, $initial_contents); |
| 08 | |
| 09 | // 取得 Unix 現在的 timestamp |
| 10 | $current = time(); |
| 11 | $salt = $current; |
| 12 | $password = "Octavia"; |
| 13 | |
| 14 | // mhash_keygen_s2k 函式根據指定的散列函數及用戶提供的密碼產生一個密鑰 |
| 15 | $hash = mhash_keygen_s2k(MHASH_GOST, $password, $salt, 20); |
| 16 | |
| 17 | // 串聯 $salt 和 $hash |
| 18 | $key = $salt . "|" . bin2hex($hash); |
| 19 | |
| 20 | $encrypted_file = @fopen('encrypted.txt','w'); |
| 21 | $ok_encrypt = @fwrite($encrypted_file, 'mhash: '.bin2hex($encrypted).' mhash_keygen_s2k:'.$key); |
| 22 | |
| 23 | if ($ok_encrypt) { |
| 24 | echo '成功建立密文檔案 encrypted_file.txt!!!'; |
| 25 | } |
| 26 | else { |
| 27 | echo ("檔案寫入失敗!"); |
| 28 | } |
| 29 | @fclose($encrypted_file); |
| 30 | } |
| 31 | ?> |
秘密鑰匙和 Crypt_Blowfish
秘密密鑰加密法利用一個單一的密鑰來加密和解密,所以也稱為「對稱密鑰」(symmetric key),舉例來說,常見的 DES 算法就是一個祕密密鑰算法。PEAR 的 Crypt_Blowfish 套件基於 Blowfish 區塊加密算法,提供雙向加密功能,無論是有或沒有祕密密鑰。這個套件無需依賴 MCrypt,但若果有的話 Crypt_Blowfish 可以使用它,這個套件最新的穩定版本是 1.0.1,安裝程序跟其他 PEAR 套件沒有分別:
> pear install pear_package_name
這個套件採用了兩個在 blowfish.php 中定義的類,所有使用 Crypt_Blowfish 套件的程式均需 include 這個檔案:
| PHP | | copy code | | ? |
| 1 | require_once 'Crypt/Blowfish.php'; |
以下熟悉的加密原碼示範 Crypt_Blowfish 的用法:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | |
| 03 | require_once 'Crypt/Blowfish.php'; |
| 04 | |
| 05 | $file = 'textfile.txt'; |
| 06 | $initial_contents = file_get_contents($file); |
| 07 | |
| 08 | if ($initial_contents) { |
| 09 | $bf = new Crypt_Blowfish('some secret key!'); |
| 10 | |
| 11 | // 加密一個字串 |
| 12 | $encrypted = $bf->encrypt($initial_contents); |
| 13 | |
| 14 | $encrypted_file = @fopen('encrypted.txt','w'); |
| 15 | $ok_encrypt = @fwrite($encrypted_file,$encrypted); |
| 16 | if ($ok_encrypt) { |
| 17 | echo '成功建立密文檔案 encrypted_file.txt!!!'; |
| 18 | } |
| 19 | else { |
| 20 | echo ("檔案寫入失敗!"); |
| 21 | } |
| 22 | @fclose($encrypted_file); |
| 23 | |
| 24 | // 把一個已加密的字串解密 |
| 25 | $plaintext = $bf->decrypt($encrypted); |
| 26 | $newfile = @fopen('newfile.txt','w'); |
| 27 | $ok_decrypt = @fwrite($newfile,$plaintext); |
| 28 | if ($ok_decrypt) { |
| 29 | echo '成功建立解密文件 newfile.txt!!!'; |
| 30 | } |
| 31 | else { |
| 32 | echo ("檔案寫入失敗!"); |
| 33 | } |
| 34 | |
| 35 | @fclose($newfile); |
| 36 | } |
| 37 | ?> |
PEAR 的 Crypt_RSA PEAR 套件讓你用任意長度的密鑰來加密數據
這個套件是以 RSA 區塊加密技術為基礎,支持雙向加密,它支援任意長度的密鑰來加密和解密,最新的 1.0.0 穩定版本可在這裡下載,並像安裝其他 PEAR 套件一樣安裝它。
> pear install pear_package_name
Crypt_RSA 需要執行密集的數學計算,並需要以下其中一個擴充模組:
- PECL big_int extension (需要版本 1.0.3 或以上)
- PHP GMP extension
- PHP BCMath extension for PHP4/PHP5,由於只有 PHP 4.0.4 綑綁了 libbcmath,所以需要這個模組
以下是使用這個套件的範例:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | require_once 'Crypt/RSA.php'; |
| 03 | |
| 04 | // 產生一雙對稱密鑰 |
| 05 | function generate_key_pair() { |
| 06 | global $public_key,$private_key; |
| 07 | $key_pair = new Crypt_RSA_KeyPair(32); |
| 08 | |
| 09 | // 從這雙密鑰中提取公鑰 |
| 10 | $public_key = $key_pair->getPublicKey(); |
| 11 | |
| 12 | // 從這雙密鑰中提取密鑰 |
| 13 | $private_key = $key_pair->getPrivateKey(); |
| 14 | } |
| 15 | |
| 16 | // 監察執行時錯誤 |
| 17 | function check_error(&$obj) { |
| 18 | if ($obj->isError()) { |
| 19 | $error = $obj->getLastError(); |
| 20 | switch ($error->getCode()) { |
| 21 | case CRYPT_RSA_ERROR_WRONG_TAIL : |
| 22 | // 無須做任何事 |
| 23 | break; |
| 24 | default: |
| 25 | // 顯示錯誤訊息然後退出 |
| 26 | echo 'error: ', $error->getMessage(); |
| 27 | exit; |
| 28 | } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | $file = 'textfile.txt'; |
| 33 | |
| 34 | generate_key_pair(); |
| 35 | $plain_text = file_get_contents($file); |
| 36 | |
| 37 | // 把公鑰表達為一個字符串 |
| 38 | $key = Crypt_RSA_Key::fromString($public_key->toString()); |
| 39 | |
| 40 | $rsa_obj = new Crypt_RSA; |
| 41 | check_error($rsa_obj); |
| 42 | |
| 43 | // 用密鑰 $key 加密 $plain_text |
| 44 | $encrypted = $rsa_obj->encrypt($plain_text, $key); |
| 45 | |
| 46 | $encrypted_file = @fopen('encrypted.txt','w'); |
| 47 | $ok_encrypt = fwrite($encrypted_file,$encrypted); |
| 48 | if ($ok_encrypt) { |
| 49 | echo '成功建立密文檔案 encrypted_file.txt!!!'; |
| 50 | } |
| 51 | else{ |
| 52 | echo ("檔案寫入失敗!"); |
| 53 | } |
| 54 | @fclose($encrypted_file); |
| 55 | |
| 56 | $enc_text = $encrypted; |
| 57 | |
| 58 | // 把密鑰表達為一個字符串 |
| 59 | $key2 = Crypt_RSA_Key::fromString($private_key->toString()); |
| 60 | check_error($key2); |
| 61 | |
| 62 | // 檢查加密/解密函式的行為 |
| 63 | $rsa_obj->setParams(array('dec_key' => $key2)); |
| 64 | check_error($rsa_obj); |
| 65 | |
| 66 | // 解密 $enc_text |
| 67 | $decrypted = $rsa_obj->decrypt($enc_text); |
| 68 | |
| 69 | $newfile = @fopen('newfile.txt','w'); |
| 70 | $ok_decrypt = @fwrite($newfile,$decrypted); |
| 71 | if ($ok_decrypt) { |
| 72 | echo '成功建立解密文件 newfile.txt!!!'; |
| 73 | } |
| 74 | else { |
| 75 | echo ("檔案寫入失敗!"); |
| 76 | } |
| 77 | @fclose($newfile); |
| 78 | |
| 79 | ?> |
用 Crypt_HMAC 來產生散列值
PEAR 的 Crypt_HMAC 套件有一個類可以用來計算 RFC 2104 兼容的散列值,Crypt_HMAC 很容易使用,你只需告訴它你的密鑰、散列計算方法、和明文。Crypt_HMAC 支援 MD5 和 SHA-1 算法,最新的穩定版本是 1.0.0,它跟其他 PEAR 套件的安裝方法沒有分別:
> pear install pear_package_name
以下是使用 Crypt_HMAC 產生散列值的範例:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | require_once 'Crypt/HMAC.php'; |
| 03 | |
| 04 | // 把字符 "0x0b" 重複二十次來產生一個密鑰 |
| 05 | $key = str_repeat(chr(0x0b), 20); |
| 06 | |
| 07 | // 產生一個 Crypt_HMAC 類的實體 |
| 08 | $crypt = new Crypt_HMAC($key, 'md5'); |
| 09 | |
| 10 | // 散列函式 |
| 11 | echo $crypt->hash('Hello'); |
| 12 | |
| 13 | $key = str_repeat(chr(0xaa), 10); |
| 14 | $data = str_repeat(chr(0xdd), 50); |
| 15 | |
| 16 | // 把散列函式的密鑰設定為 $key |
| 17 | $crypt->setKey($key); |
| 18 | echo $crypt->hash($data)."\n"; |
| 19 | ?> |
使用 PEAR 的 Crypt_DiffieHellman 套件來產生密鑰
這個 PEAR 套件在 PHP 5 環境下實作 Diffie-Hellman 密鑰交換協議,你可以使用這個協議來產生一個密鑰,並把密鑰交給兩伙人,讓他們在一個非安全通道上通訊,你可以在這裡下載最新的 0.2.1 (beta) 版本,它跟其他 PEAR 套件的安裝方法沒有分別:
> pear install pear_package_name
以下兩個程式示範怎樣為兩伙人 (subject_1 和 subject_2) 產生密鑰,第一個程式示範用 Diffie Hellman 算法產生密鑰的最簡單方法:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | // 引入 Diffie Hellman 函式 |
| 03 | require_once 'Crypt/DiffieHellman.php'; |
| 04 | |
| 05 | // 為兩夥人設定所需的引數 |
| 06 | $subject_1 = array('prime'=>'123', 'generator'=>'7', 'private'=>'3'); |
| 07 | $subject_2 = array('prime'=>'123', 'generator'=>'7', 'private'=>'34'); |
| 08 | |
| 09 | // 運用 Diffie Hellman 算法 |
| 10 | $subject_1_GK = new Crypt_DiffieHellman( |
| 11 | $subject_1['prime'], $subject_1['generator'], |
| 12 | $subject_1['private']); |
| 13 | $subject_2_GK = new Crypt_DiffieHellman( |
| 14 | $subject_2['prime'], $subject_2['generator'], |
| 15 | $subject_2['private']); |
| 16 | |
| 17 | // 產生密鑰 |
| 18 | $subject_1_GK->generateKeys(); |
| 19 | $subject_2_GK->generateKeys(); |
| 20 | |
| 21 | // 計算密鑰 |
| 22 | $subject_1_SK = $subject_1_GK->computeSecretKey( |
| 23 | $subject_2_GK->getPublicKey())->getSharedSecretKey(); |
| 24 | $subject_2_SK = $subject_2_GK->computeSecretKey( |
| 25 | $subject_1_GK->getPublicKey())->getSharedSecretKey(); |
| 26 | |
| 27 | // 顯示密鑰 |
| 28 | echo('Subject_1_SK:'.$subject_1_SK); |
| 29 | echo('Subject_2_SK:'.$subject_2_SK); |
| 30 | ?> |
第二個程式示範用 Diffie Hellman 的 BINARY 模式產生密鑰:
| PHP | | copy code | | ? |
| 01 | <?php |
| 02 | // 引入 Diffie Hellman 函式 |
| 03 | require_once 'Crypt/DiffieHellman.php'; |
| 04 | |
| 05 | // 為兩夥人設定所需的引數 |
| 06 | $subject_1 = array( |
| 07 | 'prime' => '9568094558049898340935098349053', |
| 08 | 'generator'=>'2', |
| 09 | 'private' => '2232370277237628823279273723742872289398723'); |
| 10 | $subject_2 = array( |
| 11 | 'prime' => '9568094558049898340935098349053', |
| 12 | 'generator'=>'2', |
| 13 | 'private' => '0389237288721323987429834389298232433363463'); |
| 14 | |
| 15 | // 運用 Diffie Hellman 算法 |
| 16 | $subject_1_GK = new Crypt_DiffieHellman( |
| 17 | $subject_1['prime'], $subject_1['generator'], |
| 18 | $subject_1['private']); |
| 19 | $subject_2_GK = new Crypt_DiffieHellman( |
| 20 | $subject_2['prime'], $subject_2['generator'], |
| 21 | $subject_2['private']); |
| 22 | |
| 23 | // 產生密鑰 |
| 24 | $subject_1_GK->generateKeys(); |
| 25 | $subject_2_GK->generateKeys(); |
| 26 | |
| 27 | // 用 BINARY 模式計算密鑰 |
| 28 | $subject_1_SK = $subject_1_GK->computeSecretKey( |
| 29 | $subject_2_GK->getPublicKey(Crypt_DiffieHellman::BINARY), |
| 30 | Crypt_DiffieHellman::BINARY)-> |
| 31 | getSharedSecretKey(Crypt_DiffieHellman::BINARY); |
| 32 | $subject_2_SK = $subject_2_GK->computeSecretKey( |
| 33 | $subject_1_GK->getPublicKey(Crypt_DiffieHellman::BINARY), |
| 34 | Crypt_DiffieHellman::BINARY)-> |
| 35 | getSharedSecretKey(Crypt_DiffieHellman::BINARY); |
| 36 | |
| 37 | // 顯示密鑰 |
| 38 | echo('subject_1_SK:'.$subject_1_SK); |
| 39 | echo('subject_2_SK:'.$subject_2_SK); |
| 40 | ?> |
使用以上提及的各式各樣加密方案,你應該可以做到任何想做的事,加密是一個敏感的安全問題,正如你見到的,解決方案和實作都很多,本文的資訊可以幫助你認識這個課題,但要更進一步,只有在系統安全上的經驗和努力工作,才能幫助你選擇合適的加密方案,使你在安全性、速度、和實作時間上取得平衡。
