最近兩年一直從事與金融相關(guān)項(xiàng)目的開發(fā)與維護(hù)。但是,關(guān)于 PHP 加密解密的最佳實(shí)踐,網(wǎng)上沒有人給出一個完美的總結(jié)。恰逢最近看了《圖解密碼技術(shù)》一書,對 PHP 加解密有了更深刻的認(rèn)識。
成都創(chuàng)新互聯(lián)是一家專業(yè)提供克東企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、H5網(wǎng)站設(shè)計(jì)、小程序制作等業(yè)務(wù)。10年已為克東眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。
為了避免各位看枯燥的文字理論,開篇我就把總結(jié)給出:
一、對稱加密
對稱加密的特點(diǎn)是加解密速度快,加密后的密文強(qiáng)度目前還沒有硬解的可能性。但是,在未來隨著計(jì)算機(jī)性能的提升有可能會出現(xiàn)被破解的可能性。
對稱加密的缺點(diǎn)也很明顯。對稱加密的加密過程與解密過程使用的是同一把密鑰。一旦泄漏密鑰,加密就失去了任何意義。
根據(jù)《圖解密碼技術(shù)》一書的推薦,對稱加密目前推薦使用 AES。在 PHP 當(dāng)中要實(shí)現(xiàn) AES 加解密,是使用 openssl 擴(kuò)展來實(shí)現(xiàn)。所以,請確保你的 PHP 已經(jīng)開啟了 openssl 擴(kuò)展。
可以通過如下方式檢測:
或者如下方式檢測:
AES 的加密模式屬于分組密碼模式。所謂分組密碼,是加密時把明文按照固定的長度分組,然后再進(jìn)行加密。當(dāng)然,細(xì)節(jié)之處很很多不同。AES 分組模式有多種:ECB、CBC、CFB、OFB、CTR 五種分組模式。目前優(yōu)先推薦使用 CBC 模式。
如果使用 CBC 模式,那么在加密的時候,就需要一個前置的加密向量 IV。當(dāng)初博主在使用 AES 來加密的時候,就很奇怪一個對稱加密為何要這個向量。因?yàn)椋诓┲骱臐撘庾R里,對稱加密只需要一個密鑰就 Ok 了。沒想到 AES 加密還有多種模式,而這個 CBC 模式恰恰就需要一個這樣的向量值。關(guān)于這個向量大家可以在網(wǎng)上查閱相關(guān)的資料。這個東西非常重要,也非常好理解。
關(guān)于 PHP AES 加解密會用到的相關(guān)方法:
AES 支持三種強(qiáng)度:128、192、256。128 位的強(qiáng)度最低,但是,加密解密速度較快。256 位強(qiáng)度最高,但是,加密解密速度最低。所以,大家根據(jù)自己系統(tǒng)的重要程度選擇使用對應(yīng)強(qiáng)度。通常普通的金融項(xiàng)目使用 192 位完整夠用了。頂級的就用 256 位。其他的就用 128 位吧。
二、非對稱加密
非對稱加密是指公鑰加密私鑰解密,私鑰加密公鑰解密的算法。非對稱加密的算法有很多?!秷D解密碼技術(shù)》一書推薦使用 RSA 算法。它使用起來也非常簡單。
要使用 RSA 算法。首先,我們必須生成一對公鑰私鑰。其實(shí)生成公鑰私鑰很簡單。
在 Linux 系統(tǒng),直接使用如下命令生成:
此命令會生 ~/.ssh/ 目錄下生成兩個文件:
id_rsa 是私鑰, is_rsa.pub 是公鑰。
關(guān)于 PHP RSA 加解密會用到的相關(guān)方法:
以上就是關(guān)于在 PHP 項(xiàng)目開發(fā)中,我們使用的加密解密算法的一個總結(jié)。博主寒冰在總結(jié)過程中難免會有不足之處,還請大家指正!謝謝!
使用:
$pri_key ="";
$pub_key = "";
$char = '方方塊兒';//要加密的字符
$sign = $this-RsaEncrypt($char,$pri_key);//加密結(jié)果
$result = $this-RsaDecrypt($sign,$pub_key);//對加密結(jié)果進(jìn)行解密
加密:
public function RsaEncrypt($str,$pri_key){
$pi_key =openssl_pkey_get_private($pri_key);
if(!$pi_key)return false;//秘鑰不可用
openssl_private_encrypt($str,$encrypted,$pi_key);
$encrypted =base64_encode($encrypted);
return $encrypted;
}
解密:
public function RsaDecrypt($str,$pub_key){
$pu_key =openssl_pkey_get_public($pub_key);
if(!$pu_key)return false;//秘鑰不可用
openssl_public_decrypt(base64_decode($str),$decrypted,$pu_key);
return $decrypted;
}
注:開啟PHP的php_openssl擴(kuò)展
這個跟php沒有關(guān)系,單純的是兩個密碼學(xué)的算法。如果真想搞清楚區(qū)別,你需要有密碼學(xué)的基礎(chǔ)知識。
我簡單說一下,這兩個都是標(biāo)準(zhǔn)的密碼學(xué)算法,應(yīng)用廣泛。AES是一個對稱加密算法,常常用于對數(shù)據(jù)進(jìn)行加密,RSA是一個非對稱(公鑰)加密算法,常常用于對AES加密用的密鑰進(jìn)行加密,或者進(jìn)行數(shù)字簽名等。
至于對稱加密算法和非對稱加密算法的區(qū)別說起來就越來越多了。你只要知道以下事實(shí)就好:
對稱加密算法加解密密鑰相同,而非對稱加密算法加解密密鑰不同
對稱加密算法相對于非對稱加密算法而言往往加解密速度很快
非對稱加密算法具有任何有公鑰的人都能加密數(shù)據(jù),但是只有有私鑰的人才能解密數(shù)據(jù)的特點(diǎn)
附上出處鏈接:
四,用PHP生成密鑰
PEAR::Crypt_RSA的Crypt_RSA_KeyPair類可以生成密鑰。調(diào)用步驟如下:
require_once('Crypt/RSA.php');
$math_obj = Crypt_RSA_MathLoader::loadWrapper();
$key_pair = new Crypt_RSA_KeyPair($key_lenth);
if (!$key_pair-isError()){
$public_key = $key_pair-getPublicKey();
$private_key = $key_pair-getPrivateKey();
$e =$math_obj-hexstr($math_obj-bin2int($public_key-getExponent()));
$d =$math_obj-hexstr($math_obj-bin2int($private_key-getExponent()));
$n =$math_obj-hexstr($math_obj-bin2int($public_key-getModulus()));
}
hexstr()是自己添加的函數(shù),用來把十進(jìn)制字符串轉(zhuǎn)換為十六進(jìn)制。對Crypt_RSA_Math_GMP很簡單,只需:
function hexstr($num){
return gmp_strval($num,16);
}
對Crypt_RSA_Math_BCMath略麻煩些:
function hexstr($num){
$result = '';
do{
$result = sprintf('%02x',intval(bcmod($num,256))).$result;
$num = bcdiv($num, 256);
}while(bccomp($num, 0));
return ltrim($result,'0');
}
五,用php生成密鑰(二)
為了提高加密速度,一般選一個較小的e。比較常用的是3、17、257、65537幾個素?cái)?shù)。
generate()生成密鑰的算法是依次計(jì)算p,q,n,e,d。因此做了如下改動,以便可以自己選e值:
原來的:
function Crypt_RSA_KeyPair($key_len, $wrapper_name = 'default', $error_handler = '')
改后增加一個參數(shù)e:
function Crypt_RSA_KeyPair($key_len, $e = null, $wrapper_name = 'default', $error_handler = '')
這個函數(shù)調(diào)用generate()。效應(yīng)地:
function generate($key_len = null)
也增加一個參數(shù)e:
function generate($key_len = null, $e = null)
把CRYPT_RSA-1.0.0的KeyPair.php中屬于generate()的245~271行改動順序,由e確定p和q:
if($e != null$this-_math_obj-cmpAbs($e,2)0)
$e = $this-_math_obj-nextPrime($this-_math_obj-dec($e));//取個素?cái)?shù)
else
{
while(true)
{
$e = $this-_math_obj-getRand($q_len, $this-_random_generator);
if ($this-_math_obj-cmpAbs($e,2)=0)
continue;
$e = $this-_math_obj-nextPrime($this-_math_obj-dec($e));
break;
}
}
do{
$p = $this-_math_obj-getRand($p_len, $this-_random_generator, true);
$p = $this-_math_obj-nextPrime($p);
do{
do{
$q = $this-_math_obj-getRand($q_len, $this-_random_generator, true);
$tmp_len = $this-_math_obj-bitLen($this-_math_obj-mul($p, $q));
if ($tmp_len $key_len)
$q_len++;
elseif ($tmp_len $key_len)
$q_len--;
} while ($tmp_len != $key_len);
$q = $this-_math_obj-nextPrime($q);
$tmp = $this-_math_obj-mul($p, $q);
} while ($this-_math_obj-bitLen($tmp) != $key_len);
// $n - is shared modulus
$n = $this-_math_obj-mul($p, $q);
// generate public ($e) and private ($d) keys
$pq = $this-_math_obj-mul($this-_math_obj-dec($p), $this-_math_obj-dec($q));
if($this-_math_obj-isZero($this-_math_obj-dec($this-_math_obj-gcd($e, $pq))))
break;
}while(true);
(網(wǎng)易的服務(wù)真體貼啊,連pre標(biāo)記里面的東西都給改。還改不好)這樣,如果要生成e為3的1024位密鑰,可以如下調(diào)用:
$key_pair = new Crypt_RSA_KeyPair(1024,3);
六,干什么用
加密比較重要的數(shù)據(jù)。比如注冊時用戶輸入的密碼。
登錄時把密碼hmac一下就可以防止重放攻擊(replay attack)了。對注冊不存在這種攻擊,但有密碼泄露的危險。上傳密碼hash那點(diǎn)安全性根本不算什么。這個可以用RSA加密解決。
不過,對中間人攻擊還是沒辦法。
另外一個
php服務(wù)端與客戶端交互、提供開放api時,通常需要對敏感的部分api數(shù)據(jù)傳輸進(jìn)行數(shù)據(jù)加密,這時候rsa非對稱加密就能派上用處了,下面通過一個例子來說明如何用php來實(shí)現(xiàn)數(shù)據(jù)的加密解密
加密的第一步是生成公鑰、私鑰對,私鑰加密的內(nèi)容能通過公鑰解密(反過來亦可以)
下載開源RSA密鑰生成工具openssl(通常Linux系統(tǒng)都自帶該程序),解壓縮至獨(dú)立的文件夾,進(jìn)入其中的bin目錄,執(zhí)行以下命令:
openssl?genrsa?-out?rsa_private_key.pem?1024
openssl?pkcs8?-topk8?-inform?PEM?-in?rsa_private_key.pem?-outform?PEM?-nocrypt?-out?private_key.pem
openssl?rsa?-in?rsa_private_key.pem?-pubout?-out?rsa_public_key.pem
一般來說,加密分為兩個部分,一個是非對稱加密,一個是對稱加密,使用對稱加密加密正文信息,使用非對稱加密加密對稱加密的密鑰,然后發(fā)送加密數(shù)據(jù)(消息
摘要和數(shù)字簽名就不討論了),這是正規(guī)的數(shù)據(jù)加密策略,對稱加密默認(rèn)支持大數(shù)據(jù)分段加密策略,你只需要從接口中完成加密即可,而且對稱加密速度比非對稱加
密快很多,如果你需要使用這個策略建議使用AES。
如果你不愿意使用對稱加密,只愿意使用AES加密,那你就必須喪失速度了,而且自己處理分段加密,因?yàn)镽SA加密通常是117個字節(jié)就要分段(這
個長度可能和密鑰長度有關(guān),我是用的接口是117),你需要自己把數(shù)據(jù)變成N個117字節(jié)的數(shù)據(jù)段來完成加密,解密也需要自己完成字節(jié)拼裝。