PHP 實現非對稱加密邏輯 -實現保護用戶數據安全
在日常設計及開發中,爲確保數據傳輸和數據存儲的安全,可通過特定的算法,將數據明文加密成複雜的密文。目前主流加密手段大致可分爲單向加密和雙向加密。 單向加密:通過對數據進行摘要計算生成密文,密文不可逆推還原。算法代表:Base64,MD5,SHA; 雙向加密:與單向加密相反,可以把密文逆推還原成明文,雙向加密又分爲對稱加密和非對稱加密。 對稱加密:指數據使用者必須擁有相同的密鑰纔可以進行加密解密,就像彼此約定的一串暗號。算法代表:DES,3DES,AES,IDEA,RC4,RC5; 非對稱加密:相對對稱加密而言,無需擁有同一組密鑰,非對稱加密是一種“信息公開的密鑰交換協議”。非對稱加密需要公開密鑰和私有密鑰兩組密鑰,公開密鑰和私有密鑰是配對起來的, 也就是說使用公開密鑰進行數據加密,只有對應的私有密鑰才能解密。這兩個密鑰是數學相關,用某用戶密鑰加密後的密文,只能使用該用戶的加密密鑰才能解密。如果知道了其中一個,並 不能計算出另外一個。因此如果公開了一對密鑰中的一個,並不會危害到另外一個密鑰性質。這裏把公開的密鑰爲公鑰,不公開的密鑰爲私鑰。算法代表:RSA,DSA。
非對稱加密算法
需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。 公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密; 如果用私有密鑰對數據進行加密,那麼只有用對應的公開密鑰才能解密。 因爲加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。
注意以上的一個點,公鑰加密的數據,只有對應的私鑰才能解密
在日常使用中是醬紫的:
將私鑰private_key.pem用在服務器端,公鑰發放給android跟ios等前端
客戶端用公鑰加密過後,數據只能被擁有唯一私鑰的服務器看懂。
代碼:
<?php
namespace Classes\Third\Utils;
/**
* 非對稱加密
* @author 田小濤
* @date 2019年10月30日
* @comment 數據安全機制
*
*/
class RsaUtils
{
//公鑰
private $private;
//私鑰
private $public;
//公-私鑰存儲位置
private $keyPath;
private $openssl;
private static $_instance;
private function __construct()
{
$this->keyPath = dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'rsa';
$this->openssl = 'D:\Works\phpStudy\PHPTutorial\Apache\conf\openssl.cnf';
$this->_init();
}
public static function getInstance()
{
if( null == self::$_instance )
{
self::$_instance = new self();
}
return self::$_instance;
}
/**
* 初始化公-私鑰
* @author 田小濤
* @datetime 2019年10月30日 下午1:14:01
* @comment
*
* @return boolean
*/
private function _init()
{
if( !file_exists( $this->keyPath . DIRECTORY_SEPARATOR . 'private_key.pem' ) || !file_exists( $this->keyPath . DIRECTORY_SEPARATOR . 'public_key.pem' ) )
{
$this->initialization();
}
return true;
}
/**
* 初始化公-私鑰
* @author 田小濤
* @datetime 2019年10月30日 下午1:05:59
* @comment
*
*/
protected function initialization()
{
$config = array(
'digest_alg' => 'sha512',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'config' => $this->openssl,
);
try{
//創建密鑰對
$res = openssl_pkey_new( $config );
//生成私鑰
openssl_pkey_export($res, $privkey, null, $config);
//生成公鑰
$pubKey = openssl_pkey_get_details($res)[ 'key' ];
file_put_contents( $this->keyPath . DIRECTORY_SEPARATOR . 'private_key.pem', $privkey );
file_put_contents( $this->keyPath . DIRECTORY_SEPARATOR . 'public_key.pem', $pubKey );
$this->private = openssl_pkey_get_private( $privkey );
$this->public = openssl_pkey_get_public( $privkey );
}catch ( \Exception $e ){
}
return true;
}
/**
* 提取私鑰
* @author 田小濤
* @datetime 2019年10月30日 上午11:38:02
* @comment
*
* @return resource
*/
private function _getPrivateKey()
{
$strPrivateKey = $this->keyPath . DIRECTORY_SEPARATOR. 'private_key.pem';
$key = file_get_contents( $strPrivateKey );
return openssl_pkey_get_private( $key );
}
/**
* 提取公鑰
* @author 田小濤
* @datetime 2019年10月30日 上午11:38:15
* @comment
*
* @return resource
*/
private function _getPublicKey()
{
$strPublicKey = $this->keyPath . DIRECTORY_SEPARATOR . 'public_key.pem';
$key = file_get_contents( $strPublicKey );
return openssl_pkey_get_public( $key );
}
/**
* 獲取公鑰
* @author 田小濤
* @datetime 2019年10月30日 上午11:41:31
* @comment
*
* @return resource
*/
public function getPublicKey()
{
$this->public = $this->_getPublicKey();
return $this->public;
}
/**
* 獲取私鑰
* @author 田小濤
* @datetime 2019年10月30日 上午11:41:53
* @comment
*
* @return resource
*/
public function getPrivateKey()
{
$this->private = $this->_getPrivateKey();
return $this->private;
}
/**
* 私鑰加密
* @author 田小濤
* @datetime 2019年10月30日 上午11:43:42
* @comment
*
* @param unknown $data
*/
public function privEncrypt( $data = null )
{
if( is_null( $data ) )
{
return null;
}
if ( ( is_array( $data ) || is_object( $data ) ) && !empty( $data ) )
{
$data = json_encode( $data );
}
return openssl_private_encrypt( $data, $encrypted, $this->_getPrivateKey() ) ? base64_encode( $encrypted ) : null;
}
/**
* 公鑰加密
* @author 田小濤
* @datetime 2019年10月30日 上午11:43:42
* @comment
*
* @param unknown $data
*/
public function publicEncrypt( $data = null )
{
if( is_null( $data ) )
{
return null;
}
if ( ( is_array( $data ) || is_object( $data ) ) && !empty( $data ) )
{
$data = json_encode( $data );
}
return openssl_public_encrypt( $data, $encrypted, $this->_getPublicKey() ) ? base64_encode( $encrypted ) : null;
}
/**
* 私鑰解密
* @author 田小濤
* @datetime 2019年10月30日 上午11:47:50
* @comment
*
* @param unknown $encrypted
*/
public function privDecrypt( $encrypted = null )
{
if( is_null( $encrypted ) || !is_string( $encrypted ) || strlen( $encrypted ) <= 0 )
{
return null;
}
return ( openssl_private_decrypt( base64_decode( $encrypted ), $decrypted, $this->_getPrivateKey() ) ) ? $decrypted : null;
}
/**
* 公鑰解密
* @author 田小濤
* @datetime 2019年10月30日 上午11:47:50
* @comment
*
* @param unknown $encrypted
*/
public function publicDecrypt( $encrypted = null )
{
if( is_null( $encrypted ) || !is_string( $encrypted ) || strlen( $encrypted ) <= 0 )
{
return null;
}
return ( openssl_public_decrypt( base64_decode( $encrypted ), $decrypted, $this->_getPublicKey() ) ) ? $decrypted : null;
}
}
調用示例:
$en = RsaUtils::getInstance();
$data = [ 'id' => 1, 'name' => '張三', 'age' => 12 ];
echo '<pre>';
var_dump( $data );
echo '<pre>';
$encrypt = $en->publicEncrypt( $data );
echo '加密字符串:' . $encrypt . '<br />';
$de = $en->privDecrypt( $encrypt );
echo '<pre>';
var_dump( json_decode( $de, true ) );
echo '<pre>';
exit;
**********************
那麼該類會在初始化的時候檢測公鑰/私鑰文件是否存在 若不存在則採用重新生成的方式生成
這個時候我們就可以實現每天/固定時間更新公鑰/私鑰確保更安全的數據傳輸