(JS-PHP)使用RSA演算法進行加密通訊
轉自:https://www.cnblogs.com/flowers-yang/p/3522350.html
使用者名稱密碼明文直接POST到後端,很容易被別人從監聽到。注:包括使用MD5等雜湊函式處理後的資料,這裡也算做明文(現在MD5爆破網站已經很多了~)。對安全性要求較高的網站,比如銀行和大型企業等都會使用HTTPS對其進行加密通訊。但是由於效率原因,使用HTTPS的代價是及其昂貴的,對於訪問量稍大的網站就會造成嚴重的效能瓶頸。解決方法一般只能採用專門的SSL硬體加速裝置如F5的BIGIP等。所以很多網站選擇了模擬SSL的做法,使用RSA來對密碼等安全資訊進行公鑰加密,服務端用私鑰解密。
通常是對密碼進行加密,具體如下:
1.載入三個RSA的js庫檔案,可以到這裡下載http://www.ohdave.com/rsa/。
2.獲取祕鑰:
#1. 相關資訊:
通常情況下網站的SSL證書是由專門的CA機構(如VeriSign)頒發,同時需要交納一定數額的費用。可是對於平時開發測試或其他情況下,我們自己也可以充當CA來生成自己頒發的證書。當然與前者相比缺點很明顯:不能獲得各個瀏覽器的信任,會彈出警告提示。不過,好訊息是,對安全性要求稍低的網站現在可以考慮使用免費的CA認證(貌似是其級別最低的證書)。
跟VeriSign一樣,StartSSL(網址:http://www.startssl.com,公司名:StartCom)也是一家CA機構,它的根證書很久之前就被一些具有開源背景的瀏覽器支援(Firefox瀏覽器、谷歌Chrome瀏覽器、蘋果Safari瀏覽器等)。在2009年9月份,StartSSL竟然搞定了微軟:微軟在升級補丁中,更新了通過Windows根證書認證程式(Windows Root Certificate Program)的廠商清單,並首次將StartCom公司列入了該認證清單,這是微軟首次將提供免費數字驗證技術的廠商加入根證書認證列表中。現在,在Windows 7或安裝了升級補丁的Windows Vista或Windows XP作業系統中,系統會完全信任由StartCom這類免費數字認證機構認證的數字證書,從而使StartSSL也得到了IE瀏覽器的支援。
#2.要生成獲得證書所需的金鑰等檔案:
openssl genrsa -des3 -out server.pem 1024 openssl req -new -key server.pem -out server.csr openssl rsa -in server.pem -out server.pem
使用上面的命令就會建立一個證書申請,這裡我們會要求輸入國家、組織、姓名等資訊,但是不會要求輸入證書有效天數,因為證書有效天數是CA認證中心給我們的;然後我們會把這個生成好的cert.csr(Certificate Signing Request (CSR):證書籤名申請)發給CA認證中心。CA認證中心通過後,會反饋(通常是郵件)回來認證的資訊,再匯入即可。
把上面生成的檔案內容提交給CA,即可換取證書;若自行生成則:
openssl x509 -req -days 365 -in server.csr -signkey server.pem -out server.crt
把它們放到指定目錄(例如:/ssl/),供下一步使用。
#3.獲取十六進位制的金鑰:
資料是用ASN.1編碼過的,所以可以用openssl命令從金鑰檔案(key或pem)提取祕鑰
openssl asn1parse -out temp.ans -i -inform PEM < server.pem
3.javascript 加密程式碼:
function rsa_pwd(content){ //十六進位制公鑰 var rsa_n = "DB89C01D4550F9974C30AF5370214F3...."; setMaxDigits(131); //131 => n的十六進位制位數/2+3 var key = new RSAKeyPair("10001", '', rsa_n); //10001 => e的十六進位制 content_rsa = encryptedString(key, content); //不支援漢字 return content_rsa; }
4.php 加密/解密程式碼:
<?php /** * 公鑰加密 * * @param string 明文 * @param string 證書檔案(.crt) * @return string 密文(base64編碼) */ function publickey_encodeing($sourcestr, $fileName) { $key_content = file_get_contents($fileName); $pubkeyid = openssl_get_publickey($key_content); if (openssl_public_encrypt($sourcestr, $crypttext, $pubkeyid)) { return base64_encode("".$crypttext); } } /** * 私鑰解密 * * @param string 密文(二進位制格式且base64編碼) * @param string 金鑰檔案(.pem / .key) * @param string 密文是否來源於JS的RSA加密 * @return string 明文 */ function privatekey_decodeing($crypttext, $fileName, $fromjs = FALSE) { $key_content = file_get_contents($fileName); $prikeyid = openssl_get_privatekey($key_content); $crypttext = base64_decode($crypttext); $padding = $fromjs ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING; if (openssl_private_decrypt($crypttext, $sourcestr, $prikeyid, $padding)) { return $fromjs ? rtrim(strrev($sourcestr), "/0") : "".$sourcestr; } return ''; } ?>
5.測試程式碼:
//JS->PHP 測試 $_POST['password']是js加密後的資訊 $txt_en = $_POST['password']; $txt_en = base64_encode(pack("H*", $txt_en)); $file = 'ssl/server.pem'; $txt_de = privatekey_decodeing($txt_en, $file, TRUE); var_dump($txt_de); //PHP->PHP 測試 $data = "漢字:1a2b3c"; $config = Core::getInstance()->config; $file1 = 'ssl/server.crt'; $file2 = 'ssl/server.pem'; $a = publickey_encodeing($data, $file1); $b = privatekey_decodeing($a, $file2); var_dump($b);
6.需注意:
#1.PHP中openssl擴充套件公私鑰加密函式只支援小資料,加密時117位元組,解密時128位元組。若不然得自己迴圈加密後合併。
#2.SSL本身也只是用RSA來進行金鑰加密,資料加密則是利用這個加密的金鑰進行對稱加密,以保證速度。所以萬不可將其用於大資料量加密!
7.本方案几處優點:
#1.安全性高。基於非對稱的RSA演算法加密資料,只要在私鑰不被暴露的前提下,金鑰長度足夠長,短時間內基本是無法破解的。
#2.使用方便。前端使用現成的JS庫來實現加密,PHP端則可直接使用現成的openssl擴充套件,而不用RSA的PHP原始碼實現或自己開發擴充套件。
#3.速度靠譜。由於RSA解密演算法相當複雜,而該操作交由PHP端擴充套件來實現,效率上比網上的PHP程式碼要高許多。
#4.便於升級。金鑰是直接從linux下openssl工具生成的證書中獲取,不僅不用其他金鑰生成工具,也方便今後升級到真正的HTTPS。
8.參考文章:
http://www.jishuer.com/static/article-270.html
http://blog.csdn.net/linvo/article/details/5619807
http://blog.sina.com.cn/s/blog_4fcd1ea30100yh4s.html
http://blog.csdn.net/fenglibing/article/details/8610280