上篇文章介紹了對稱加密的原理,可是它的最大問題就是加密和解密的密鑰是相同的,而且不能保證密鑰能安全的送到雙方手裏,即便安全的送到雙方手裏,免不了內部會有"臥底"的存在php
既然有對稱加密,那麼天然會聯想到非對稱加密。非對稱加密的核心在於加密和解密使用的是不一樣的密鑰,如何作到使用不一樣的密鑰呢?
好比我有一個只能用鑰匙打開的存錢罐,平時你們只能把零錢放到儲錢罐中,可是隻有我纔有取錢的鑰匙。放到儲錢罐的硬幣能夠當作加密後的內容,而只有用鑰匙才能將"加密"後的硬幣取出來。
這樣咱們就能夠把用來加密的密鑰(公鑰)給了任何人,咱們只要本身保存好解密的密鑰(私鑰)就能夠安全的保護咱們的數據。
非對稱算法有不少:RSA、Elgamal、揹包算法、Rabin、D-H、ECC等,下面咱們來簡單介紹一下RSA算法。程序員
RSA公鑰加密算法是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一塊兒提出的。1987年首次公佈,當時他們三人都在麻省理工學院工做。RSA就是他們三人姓氏開頭字母拼在一塊兒組成的(啥時候以我名字命名一個呢)。
RSA是目前最有影響力的公鑰加密算法,它可以抵抗到目前爲止已知的絕大多數密碼攻擊,已被ISO推薦爲公鑰數據加密標準。算法
//注意:明文爲數字,實際計算過程咱們能夠經過ASCII碼轉換 密文 = (明文 ^ E) % N; //其中的E和N就是咱們的公鑰 明文 = (密文 ^ D) % N; //其中的D和N就是咱們的私鑰
公鑰和私鑰不是隨便弄幾個數字就能夠的,是通過嚴格的數學公式計算出來的。數據庫
N = P * Q;
L = (P - 1) * (Q - 1); //圖解密碼技術中說須要計算乘積以後的最小公倍數,可是通過代碼測試並不許確,哪位大俠瞭解麻煩留言告知一下~
//E須要同時知足下面兩個條件 1. 1 < E < L 2. E和L的最大公約數爲一(歐幾里得算法,這些惡魔啊,E和L必須互質,這樣才能保證必定能夠計算出私鑰D)
//D須要知足下面公式 (E * D) % L = 1; //想要保證結果爲1,E和L必須互質!!!
上面就是整個計算過程,爲了保證數據的安全現實中,P和Q會選用特別大的數(1024比特或者更大)安全
上面已經提到過加密和解密的方法,咱們用具體的數字實踐一下,加深理解吧。函數
假設:P = 七、Q = 11(均爲質數) 那麼:N = P * Q = 7 * 11 = 77
L = (P - 1) * (Q - 1) = 6 * 10 = 60
1 < E < 60 E和L的最大公約數爲一,咱們假設E=23
(23 * D) % 60 = 1; D = 47;
那麼我就獲得了公鑰(E=23,N=77),私鑰(D=47,N=77)學習
咱們假設須要加密數字:12
公式:密文 = (明文 ^ E) % N;
12 ^ 23 % 77 = 6624737266949237011120128 % 77 = 45;
這個45就是咱們加密後的密文測試
解密
公式:明文 = (密文 ^ D) % N;
45 ^ 47 % 77 = 502328880013965819626664594350710696732674427522624682751484215259552001953125 % 77 = 12;
得出原文:12加密
下面是我用PHP實現的加密&解密示例,供你們參考(由於指數運算的結果集會很大,咱們必須使用PHP中提供的BC Math系列函數計算)spa
/** * 冒牌RSA算法 * @author zhjx922 */ /** * 判斷數字是否爲質數 * @param $num * @return bool */ function isPrimeNumber($num) { $k = 0;//定義次數變量 for ($i = 1; $i <= $num; $i++) { if (bcmod($num, $i) == 0) { $k++;//若是取模等於0,次數k自加 } } if ($k == 2) { return true; } return false; } //求最小公倍數 function minMultiple($a, $b) { if($b==0) //必定要考慮除數不能爲零 { return $b; } else { $m = bccomp($a, $b) == 1 ? $a : $b; $n = bccomp($b, $a) == 1 ? $b : $a; for($i=2; ; $i++) { $mul = bcmul($m, $i); if(bcmod($mul, $n) == 0) { return $mul; } } } return bcmul($a, $b); } //求最大公約數 function maxDivisor($a,$b) { $n = bccomp($a, $b) == 1 ? $b : $a; for($i = $n; $i>1; $i--) { if(bcmod($a, $i) == 0 && bcmod($b, $i) == 0) { return $i; //此處若是用echo $i;則輸出結果爲432;故應區分echo、return的區別 } } return 1; } do{ //隨機一個質數P $p = mt_rand(101, 197); } while(!isPrimeNumber($p)); do{ //隨機一個質數Q $q = mt_rand(101, 197); } while(!isPrimeNumber($q)); $n = bcmul($p, $q); //$l = minMultiple($p - 1, $q - 1); //經測試不可用 $l = bcmul($p - 1, $q - 1); do { $e = mt_rand(2, $l - 1); }while(maxDivisor($e, $l) != 1); $d = 1; while(bcmod(bcmul($e,++$d), $l) != 1) { } echo 'p:' . $p . PHP_EOL; echo 'q:' . $q . PHP_EOL; echo 'n:' . $n . PHP_EOL; echo 'l:' . $l . PHP_EOL; echo 'e:' . $e . PHP_EOL; echo 'd:' . $d . PHP_EOL; echo "公鑰:e={$e},n={$n}" . PHP_EOL; echo "私鑰:d={$d},n={$n}" . PHP_EOL; //加密 function encode($e, $n, $string) { $enString = ''; $len = strlen($string); for($i = 0; $i < $len; $i++) { $pow = bcpow(ord($string{$i}), $e); $mod = bcmod($pow, $n); $enString .= pack('L', $mod); } return $enString; } //解密 function decode($d, $n, $string) { $deString = ''; $string = unpack('L*', $string); $len = count($string); for($i = 1; $i <= $len; $i++) { $pow = bcpow($string[$i], $d); $mod = bcmod($pow, $n); $deString .= chr($mod); } return $deString; } $startTime = microtime(true); $string = '歡迎關注"僞裝是個程序員"公衆號'; echo "原文:" . $string . PHP_EOL; $encodeString = encode($e, $n, $string); echo "密文:" . $encodeString . PHP_EOL; $decodeString = decode($d, $n, $encodeString); echo "解密後:" . $decodeString . PHP_EOL; $endTime = microtime(true); echo "Total:" . ($endTime - $startTime) . 's.' . PHP_EOL;
沒有什麼加密方式能一直保持絕對的安全,尤爲經常使用的MD5,若是你的數據庫中密碼仍是使用MD5的哈希結果不要笑話人家直接用明文存密碼的人,五十步笑百步而已。。。
最近谷歌宣佈破解了SHA-1,隨着計算能力的提升,SHA-256,RSA等等也是早晚的事兒。。
歡迎關注個人公衆號,一塊兒交流學習~