PHP-密碼學算法及其應用-對稱密碼算法

轉自:http://www.smatrix.org/bbs/simple/index.php?t5662.htmlphp

//////////////////////////////////////////////////////////////////////////////
目錄
1.    PHP的散列函數及其應用
2.    PHP中的對稱密碼算法及其應用
3.    PHP的公鑰密碼算法及其應用
///////////////////////////////////////////////////////////////////////////////

2 PHP中的對稱密碼算法及其應用
前一段時間一直想寫完PHP中的密碼學算法及其應用的三大部分,但無奈雜事繁多,因此一直拖到如今,但願可以以通俗而非學術化的語言說清楚這部分。
2.1 什麼是對稱密碼算法
   前面咱們將的HASH函數並不是真正的密碼算法,而只是一個生成消息摘要的函數。由於一個密碼系統須要有五個要素部分組成(密文,明文,加密算法,解密算 法,密鑰)。區別對稱與非對稱密碼算法的關鍵在於解密密鑰是否會等於加密密鑰,若是相等,那麼就是對稱的,不相等就是非對稱的。像DES,AES都屬於對 稱密碼算法,而RSA,ECC都屬於非對稱的公鑰密碼算法。通常而言,對稱密碼算法加解密速度較快,一般是用來進行數據加密,而非對稱的公鑰加密算法加解 密速度較慢,一般是用來進行數字簽名和構造複雜的密碼協議。坊間傳說的某某公鑰加密算法比某某對稱加密算法更好更安全實屬荒謬,這就比如討論你的手和腳那 個更有用。
2.2 什麼叫作對稱密碼算法的加密模式
這個是不少開發人員搞得不太清楚的東西,畢竟大部分PHP開發者未必都接觸過密碼學方 面的專業培訓,所以看到PHP的相關函數感受望而生畏,不知所措。所謂加密模式就是加密任意長度的消息的使用密碼算法的方法。一般對稱密碼算法都會提供至 少四種基本工做模式,其中兩組爲塊模式(ECB和CBC),兩組是流模式(CFB和OFB)。打個通俗的比方,密碼算法函數就好像個機器,明文就好像原材 料,機器不可能一次處理全部明文,那麼塊模式的策略就是把明文進行分組,而後按照必定的規則放入機器作處理,而後獲得一組密文。ECB模式就是將消息分紅 相互獨立的塊,每一塊都用同一算法和密鑰加密(好比使用DES),塊與塊之間沒有關聯。而CBC也是將消息分紅相互獨立塊,可是加密的時候,作了點手腳, 使得下一塊的密文和上一塊的密文有關係,分塊加密前與上一塊的密文進行異或運算,而後在用同一算法和密鑰加密,因爲第一塊明文沒有上一塊的密文,因此必須 人工指定一個,稱之爲初始化向量。而CFB和OFB模式把明文看做是二進制流,實際上也是分塊的,它們都須要一個初始化向量,不斷地生成僞隨機二進制流, 而後與明文分組進行異或運算,而密文是否參與運算是CFB和OFB之間的區別所在。工做模式詳情具體很差用文字描述,有興趣的不妨密碼學方面的書籍(安全 矩陣論壇上不少,隨便下一本都有)。
2.3 PHP中的對稱密碼算法函數
PHP的各個版本都提供了相應的對稱密碼算法函數,因此PHP的 開發者們徹底不必本身山寨一個,請相信這些提供的對稱密碼算法函數絕對比你本身設計的更強大。目前爲止,不絕於耳的是各類顏色的客(還有祖傳的)聽說能 夠幾分鐘或者秒破一個密碼算法,其實這是誤解,除了在看雪論壇上看到幾個直接針對密鑰的暴力破解實例外,偶在網絡上不多見過數學上能攻破一個密碼算法的 人。之因此係統或軟件被破解,是由於密碼算法使用不當或者攻擊者繞過了密碼算法機制。PHP的主要對稱密碼算法函數來自於Mcrypt函數族。固然這個函 數族是PHP的可選部件,須要下載libmcrypt-x.x.tar.gz並在PHP.INI中進行配置。你能夠經過phpinfo()來看看本身配置 的環境是否支持這些函數。html

mcrypt support: enabled
Version:  2.5.8  
Api No: 20021217  
Supported ciphers:  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  
Supported modes:  cbc cfb ctr ecb ncfb nofb ofb stream  


Supported ciphers中列出的就是支持的算法,算法不少,很好很強大,其中rijndael開頭的就是AES,因此PHP沒有AES實屬繆傳。接下來咱們介紹如 何正確的使用PHP的對稱密碼算法函數。首先咱們看到這個函數mcrypt_module_open,原型是:算法

resource mcrypt_module_open ( string algorithm, string algorithm_directory, string mode, string mode_directory)


這 個函數的做用其實是建立一個使用對稱加密算法的工做環境(理解這個很重要,若是你學過windows程序設計,這種思想隨處可見),告訴PHP要使用何 種算法(第一個參數),算法路徑在哪裏(第二個參數),採用何種工做模式(第三個參數),工做模式描述的路徑在哪裏(第四個參數).實際使用中咱們通常不 須要設置第二個參數和第四個參數。如今咱們看個例子:windows

<?php
    $key = "security matrix";  //密鑰
    $input = "mathmatica sounds very terrible!"; //明文
    
    //加密
    $td = mcrypt_module_open('rijndael-256', '', 'cbc', ''); //建立加密環境
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); //因爲採用了cbc模式,咱們須要一個初始化向量iv。
    mcrypt_generic_init($td, $key, $iv); //初始化加密算法,當算法模式是ecb時候,會自動忽略iv
    $encrypted_out_data = mcrypt_generic($td, $input);  //執行加密
    mcrypt_generic_deinit($td);  //釋放加密算法資源      
    mcrypt_module_close($td);    //關閉加密環境,釋放資源
    echo "明文是:$input";
    echo '<br>';
    echo "密鑰是:$key";
    echo '<br>';
    echo "密文是:$encrypted_out_data";
    echo '<br>';
    //解密,因爲$iv沒有被清掉,因此咱們還能夠繼續用這個。實際使用中必定要保存起來
    $td = mcrypt_module_open('rijndael-256', '', 'cbc', ''); //建立加密環境
    mcrypt_generic_init($td, $key, $iv);
    $decrypted_out_data=mdecrypt_generic($td,  $encrypted_out_data);
    mcrypt_generic_deinit($td);  //釋放加密算法資源      
    mcrypt_module_close($td);    //關閉加密環境,釋放資源
    
    echo '<br>';
    echo "密鑰是:$key";
    echo '<br>';
    echo "明文是:$decrypted_out_data";


這 段源代碼我添加的註釋與PHP手冊中的說法略有不一樣,主要是我爲了讓你們更明白些。這裏有幾個函數,第一個是mcrypt_create_iv,這個是用 來建立一個隨機的初始化向量IV,四種基本模式中,除了ECB,均要有初始化向量IV。mcrypt_generic_init函數用來初始化密碼算法對 象,包括具體算法的句柄,密鑰和初始化向量。當算法模式是ECB時,該函數會自動忽略初始化向量IV。接下來利用mcrypt_generic執行加密。 加密完成後利用mcrypt_generic_deinit和mcrypt_module_close來釋放環境。這個過程和windows程序設計下面 使用設備資源的過程很是類似。值得注意的是,因爲IV生成的時候使用了隨機數,因此每次是不同的,也會致使密文不同。所以除了ECB模式外,其它三種 模式均要把IV記下了,才能進行解密。解密過程和加密過程是同樣的,只是函數mcrypt_generic換成了mdecrypt_generic,請注 意,別眼花,注意他們的書寫上的區別,前者是加密,後者是解密。這個過程是PHP中標準的使用對稱密碼算法的過程。
PHP中有些很是陌生的密碼算法,好比gost,除非專業搞密碼算法的,通常不多人會知道這個算法的具體內容。咱們可使用如下PHP的算法信息函數來了解一個算法:數組

mcrypt_enc_get_algorithms_name –獲取算法具體名稱
mcrypt_enc_get_block_size – 獲取算法的分組大小
mcrypt_enc_get_iv_size – 獲取算法的IV初始化向量大小
mcrypt_enc_get_key_size – 獲取算法支持的最大密鑰長度
mcrypt_enc_get_modes_name – 獲取算法支持的加密模式
mcrypt_enc_get_supported_key_sizes –返回一個數組,裏面有算法支持的各類密鑰長度 


使用方法以下:安全

<?php
    $td = mcrypt_module_open (MCRYPT_BLOWFISH, '', 'ecb', '');
    echo mcrypt_enc_get_algorithms_name($td). "\n";
    echo mcrypt_enc_get_block_size($td). "\n";
    echo mcrypt_enc_get_iv_size($td). "\n";
    echo mcrypt_enc_get_key_size($td). "\n";
    echo mcrypt_enc_get_modes_name($td). "\n";
    //結果:Blowfish 8 8 56 ECB


PHP中除了上述用法以外,還有一種加密解密的用法。依賴於函數mcrypt_encrypt和mcrypt_decrypt,前者用於加密,後者用於解密。其函數原型爲:網絡

string mcrypt_encrypt ( string cipher, string key, string data, string mode [, string iv])
string mcrypt_decrypt ( string cipher, string key, string data, string mode [, string iv])


咱們很容易發現,兩個函數的參數都是徹底同樣的,實際上也只有同樣才能正常的加密和解密。這種用法與前述函數用法的區別在於這兩個函數把全部的算法信息看成爲本身的參數了,習慣於傳統PHP開發的人會以爲這個更加易用。先看個代碼:函數

<?php
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);  //獲取該算法在OFB模式下的IV向量大小
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); //建立一個初始化IV向量
    $key = "security matrix";   //密鑰
    $plaintext = "you should arrive here in eight clock"; //明文
    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_CBC, $iv); //直接執行加密
    echo  "明文是:$plaintext.<br> 密文是: $ciphertext";

    $outtext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key,$ciphertext, MCRYPT_MODE_CBC, $iv); //執行解密
    echo "<br>";
    echo  "解密後的明文是:  $outtext<br>";
    //結果:
    // 明文是:you should arrive here in eight clock.
    //密文是: p 蜉~嵁'mh!Bl廀'Es??{ ?e斤舉* YC¬濤溋C海Rc敻J 偰k躕"蟸v勸
    //解密後的明文是: you should arrive here in eight clock


除了上面兩種全面的用法,PHP還提供了四種直接指定加密模式的加密函數mcrypt_cfb(), mcrypt_cbc(), mcrypt_ecb()和 mcrypt_ofb(),這四個函數的原型幾乎是同樣的。以mcrypt_cbc()爲例,原型以下:加密

string mcrypt_cbc ( int cipher, string key, string data, int mode [, string iv])
string mcrypt_cbc ( string cipher, string key, string data, int mode [, string iv])


其中第一個參數指定算法,第二個參數指定密鑰,第三個參數指定數據(根據後面的mode決定是明文仍是密文),第四個參數mode指定是加密操做仍是解密操做,第五個參數是可選的,若是須要IV則必定要IV.仍是看個例子,你會發現如此簡單:spa

<?php
$key = "security matrix";
$text = "fleshwound is a farmer,deadly wounded ";//明文

$ctext = mcrypt_ecb (MCRYPT_3DES, $key, $text, MCRYPT_ENCRYPT); //加密
$ptext = mcrypt_ecb (MCRYPT_3DES, $key, $ctext, MCRYPT_DECRYPT); //解密

echo "明文:$text <br> 密文:$ctext <br>解密後明文:$ptext"
//結果:
//明文:fleshwound is a farmer,deadly wounded
//密文:皚剢GsXe?+ ?v?3佉:?縞2 拉哣 磀毄+?
//解密後明文:fleshwound is a farmer,deadly wounded 

好 了,寫了幾個小時,總算把PHP中對稱密碼算法介紹完了,其實你們會發現使用仍是很是簡單的,不會比寫一個分頁函數難,若是可以在本身項目中得當地使用, 將會使得系統安全性大大提升。並且聰明的童鞋能夠從這裏找到對PHP源代碼自加密自解密的代碼生成方法(比ZEND的更具備靈活性),固然也能夠用來作壞 事,實現網頁級的變形免殺木馬,具體方法我不介紹了。下一個專題是PHP中的公鑰密碼算法,我會盡可能將PHP的密碼學應用用通俗的方法逐步的引向深刻。