加密算法有很多,如不可逆的摘要算法MD5、SHA(安全哈希算法),可逆的Base64編碼,對稱加密算法DES、AES,還有非對稱加密算法DH、RSA等。那是不是說明我們可以使用任何一種加密算法就能保證網站的安全性,答案是否。舉個例子,我們在登錄web頁面時,發送用戶名和密碼給服務器,這時請求被攔截了:
(1) 密碼采用不可逆加密。因為不可逆加密算法就幾種,不管怎樣變換,攻擊者都可以模擬登錄;
(2) 密碼采用可逆加密算法。因為加密是在前端進行加密,所以可能通過js代碼看到加密方式,這樣也就很容易破解了。
所以針對瀏覽器-服務器這種模式,最好采用非對稱加密算法,即使攻擊者知道了加密算法和公鑰,他也沒法解密,因為用公鑰加密的數據只能用私鑰才能解密,而且私鑰始終保存在服務器,攻擊者無法獲取。
今天總結一下項目中使用非對稱加密算法RSA實現登錄驗證的過程。
一、RSA非對稱加密算法介紹
RSA加密算法是一種非對稱加密算法,在甲乙方的通信過程中,首先甲方(通常是服務端)會生成一對秘鑰對,稱作公鑰(Public Key)和私鑰(Private Key),然后他把生成的公鑰或私鑰發送給乙方(客戶端),乙方獲取到公鑰或私鑰之后,用它將原始數據加密后發送給甲方,此時甲方用私鑰或公鑰對乙方發送過來的加密數據進行解密,在整個通信過程中,即使請求被攔截,攻擊者獲取到了暴露的秘鑰和加密數據也無法解密,因為只能使用甲方手中的私鑰才能解密,而在整個通信過程中私鑰始終保存在甲方手中,沒有在通信過程中傳遞,所以攻擊者也就很難獲取到,除非是甲方有意暴露。如下圖所示:

上圖的過程描述為:
1、 web服務器生成密鑰對
2、 客戶端(瀏覽器)從服務器獲取公鑰
3、 客戶端用獲取的公鑰對原始數據加密后發送給web服務器
4、 Web服務器接受加密后的數據並獲取到私鑰
5、 Web服務器用私鑰對數據進行解密
在上圖中,采用的是公鑰加密,私鑰解密,還可以使用私鑰加密,公鑰解密的方式,關鍵是服務器只暴露其中的一個秘鑰給客戶端進行加密,另一個自己保存用來解密。
二、用PHP實現RSA加解密過程
第一步:生成密鑰文件(公鑰和私鑰)
注:在生成秘鑰對之前,需要在linux系統中安裝openssl。
1、生成私鑰文件

2、利用私鑰生成公鑰文件

3、秘鑰文件生成結果

第二步:生成密鑰對(公鑰和私鑰)
1、根據私鑰文件生成私鑰
function get_private_key() { return @file_get_contents(DATA_PATH . 'Cert/rsa_private_key.pem'); }
2、根據公鑰文件生成公鑰
function get_public_key() { return @file_get_contents(DATA_PATH . 'Cert/rsa_public_key.pem'); }
第三步:封裝加密解密方法
1、公鑰加密
/** * 公鑰加密 */ function rsa_encrypt($string) { $publicKey = get_public_key(); $resource = openssl_pkey_get_public($publicKey); if (!is_resource($resource)) { return ''; } $encrypted = ""; openssl_public_encrypt($string, $encrypted, $resource); return $encrypted; }
2、私鑰解密
/** * 私鑰解密 */ function rsa_decrypt($string) { $privateKey = get_private_key(); $resource = openssl_pkey_get_private($privateKey); if (!is_resource($resource)) { return ''; } $decrypted = ''; try { openssl_private_decrypt($string, $decrypted, $resource); return $decrypted; } catch (Exception $e) { return ''; } }
第四步:測試結果
簡單寫一個方法進行測試
/** * 測試RSA加密解密 */ public function testEncode(){ $data = 'hello world!'; var_dump('加密前的數據:'.$data); $encodeData = rsa_encrypt($data); var_dump('公鑰加密后的數據:'.$encodeData); $decodeData = rsa_decrypt($encodeData); var_dump('私鑰解密后的數據:'.$decodeData); }
測試結果如下:

從上面結果可以看到原始數據【hello world!】解密后也是【hello world!】,說明使用RSA加解密成功,但是加密后的結果中有亂碼,這一問題的解決方式就是在加密和解密過程中使用base64編碼。
第五步:解決加密亂碼問題
1、公鑰加密(加密結果進行base64編碼)
1 /** 2 * 公鑰加密 3 */ 4 function rsa_encrypt($string) { 5 $publicKey = get_public_key(); 6 $resource = openssl_pkey_get_public($publicKey); 7 8 if (!is_resource($resource)) { 9 return ''; 10 } 11 12 $encrypted = ""; 13 openssl_public_encrypt($string, $encrypted, $resource); 14 15 return base64_encode($encrypted); 16 }
注:上面的第15行中對加密結果進行base64編碼。
2、私鑰解密(解密之前對加密數據進行base64解碼)
1 /** 2 * 私鑰解密 3 */ 4 function rsa_decrypt($string) { 5 $privateKey = get_private_key(); 6 $resource = openssl_pkey_get_private($privateKey); 7 8 if (!is_resource($resource)) { 9 return ''; 10 } 11 12 $decrypted = ''; 13 14 try { 15 openssl_private_decrypt(base64_decode($string), $decrypted, $resource); 16 17 return $decrypted; 18 } catch (Exception $e) { 19 return ''; 20 } 21 }
注:上面的第15行中,在解密之前對加密數據進行base64解碼
3、查看優化后的結果

以上就是用PHP實現的服務端加解密全部過程,需要注意以下幾點:
1、 在linux中生成密鑰文件之前需要安裝openssl庫
2、 為解決加密數據出現的亂碼問題,需要對加密后的數據進行base64編碼
三、B/S模式下的rsa加密
1、前端加密庫
PHP端的加解密,可用在第三方跟平台之前的傳輸數據,但是如果是前端傳到后端呢?我們知道前端是采用JavaScript處理業務邏輯,所以當用戶密碼等數據傳到后台服務器之前需要用JavaScript進行加密,假設后台語言是PHP,那么這就涉及加解密一致性的問題,相當於兩種語言結合實現rsa非對稱加解密過程。而JavaScript已經有了一個加密庫jsencrypt.js。
2、jsencrypt.js庫的使用
一般來說,前端加密所用到的密鑰(公鑰或私鑰)都是服務器傳過來的,前端只需要負責將原始數據加密后傳給后台就可以。這里為了介紹jsencrypt.js庫的使用,假設前端已經獲取到了公鑰和私鑰,並使用公鑰加密,私鑰解密。一般只會獲取其中的一個進行前端加密,如果是公鑰加密,服務端使用私鑰解密;如果是私鑰加密,服務端使用公鑰解密,這里是為了驗證jsencrypt.js的加解密效果獲取到公鑰和私鑰的,不要誤解。
①公鑰加密
/* 公鑰加密 */ function rsaEncrypt(valueData) { var PublicKey = $("#txt_rsa_public_key").val(); var encrypt = new JSEncrypt(); encrypt.setPublicKey(PublicKey); var encrypted = encrypt.encrypt(valueData); return encrypted; }
②私鑰解密
/* 私鑰解密 */ function rsaDecrypt(valueData) { var PrivateKey = $("#txt_rsa_private_key").val(); var decrypt = new JSEncrypt(); decrypt.setPrivateKey(PrivateKey); var decrypted = decrypt.decrypt(valueData); return decrypted; }
③測試方法
/* 測試加解密 */ function testRsa(){ var data = "hello world!"; console.info("加密前的數據:"+data); var encodeData = rsaEncrypt(data); console.info("使用公鑰加密后的數據:"+encodeData); var decodeData = rsaDecrypt(encodeData); console.info("使用私鑰解密后的數據:"+decodeData); }
④測試結果

根據上面的示例,可以總結用JS實現ras加解密的過程為:
1、 獲取JSEncrypt對象。(加密解密都使用同一個實例)
2、 給JSEncrypt對象設置私鑰/公鑰
3、 調用對象的加密解密方法
由此可以看出,前端加密不需要設置base64編碼解碼的過程。
四、web登錄過程中RSA的運用
通過以上介紹,知道了前后端怎么使用非對稱加密算法RSA進行加密解密的過程,可以利用這兩部分內容實現web登錄過程中對密碼的加密,具體的實現過程這里不再贅述,這里只介紹一下流程:
1、在你所用的web服務器(Apache、Tomcat等)指定目錄下生成私鑰文件;
2、在相同目錄下利用私鑰文件生成公鑰文件;
3、在服務端(代碼中)生成公鑰和私鑰;
4、客戶端(WEB前端)從服務端獲取公鑰;
5、將密碼等敏感信息使用公鑰加密后傳給后台(ajax方式);
6、服務端獲取到加密數據后使用自己的私鑰進行解密;
以上就是加解密的完整過程,至於其他的密碼是否正確之類的驗證,就是其他的業務邏輯了。
本次服務端使用PHP,下次介紹服務端Java的方式。
