PHP 加密:AES & RSA
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP 加密:AES & RSA相关的知识,希望对你有一定的参考价值。
参考技术A
最近两年一直从事与金融相关项目的开发与维护。但是,关于 php 加密解密的最佳实践,网上没有人给出一个完美的总结。恰逢最近看了《图解密码技术》一书,对 PHP 加解密有了更深刻的认识。
为了避免各位看枯燥的文字理论,开篇我就把总结给出:
一、对称加密
对称加密的特点是加解密速度快,加密后的密文强度目前还没有硬解的可能性。但是,在未来随着计算机性能的提升有可能会出现被破解的可能性。
对称加密的缺点也很明显。对称加密的加密过程与解密过程使用的是同一把密钥。一旦泄漏密钥,加密就失去了任何意义。
根据《图解密码技术》一书的推荐,对称加密目前推荐使用 AES。在 PHP 当中要实现 AES 加解密,是使用 openssl 扩展来实现。所以,请确保你的 PHP 已经开启了 openssl 扩展。
可以通过如下方式检测:
或者如下方式检测:
AES 的加密模式属于分组密码模式。所谓分组密码,是加密时把明文按照固定的长度分组,然后再进行加密。当然,细节之处很很多不同。AES 分组模式有多种:ECB、CBC、CFB、OFB、CTR 五种分组模式。目前优先推荐使用 CBC 模式。
如果使用 CBC 模式,那么在加密的时候,就需要一个前置的加密向量 IV。当初博主在使用 AES 来加密的时候,就很奇怪一个对称加密为何要这个向量。因为,在博主寒冰的潜意识里,对称加密只需要一个密钥就 Ok 了。没想到 AES 加密还有多种模式,而这个 CBC 模式恰恰就需要一个这样的向量值。关于这个向量大家可以在网上查阅相关的资料。这个东西非常重要,也非常好理解。
关于 PHP AES 加解密会用到的相关方法:
AES 支持三种强度:128、192、256。128 位的强度最低,但是,加密解密速度较快。256 位强度最高,但是,加密解密速度最低。所以,大家根据自己系统的重要程度选择使用对应强度。通常普通的金融项目使用 192 位完整够用了。顶级的就用 256 位。其他的就用 128 位吧。
二、非对称加密
非对称加密是指公钥加密私钥解密,私钥加密公钥解密的算法。非对称加密的算法有很多。《图解密码技术》一书推荐使用 RSA 算法。它使用起来也非常简单。
要使用 RSA 算法。首先,我们必须生成一对公钥私钥。其实生成公钥私钥很简单。
在 Linux 系统,直接使用如下命令生成:
此命令会生 ~/.ssh/ 目录下生成两个文件:
id_rsa 是私钥, is_rsa.pub 是公钥。
关于 PHP RSA 加解密会用到的相关方法:
以上就是关于在 PHP 项目开发中,我们使用的加密解密算法的一个总结。博主寒冰在总结过程中难免会有不足之处,还请大家指正!谢谢!
php RSA和AES的加密
# RSA和AES的加密
<?php
/**
* Created by PhpStorm.
* User: code
* Date: 2019/4/15
* Time: 10:55
*/
namespace App\Helper\Classes\ApiCipher;
use App\Helper\Traits\Singleton;
/**
* RSA(非对称)和AES(对称)加密数据
*
* 模拟一个https流程
*
* 有分:
* 1、服务器单向RSA + AES(已实现)
* 2、双向RSA + AES
*/
abstract class BaseRSAAndAES
{
use Singleton;
/**
* @return mixed|array
*/
abstract public function getConfig();
public function getPublicKey()
{
return $this->getConfig()['public'] ?? '';
}
/**
* 直接获取解密后的数据
*
* @param string $key 被RSA公钥加密过的AES的key,此key经过base64和url的转码
* @param string $dataString 被加密的数据
* @return array
*/
public function getData($key, $dataString = '')
{
// 有时候处理xdebug就报错
$aesKey = $this->getDecrypt(urldecode(base64_decode(urldecode($key)))); // 拿到公钥
return [
'aes' => $aesKey,
'data' => $dataString ? $this->getAESData($aesKey, $dataString) : '',
];
}
/**
* 获取AES数据
*
* @param string $aesKey AES的key
* @param string $dataString 被AES加密的数据
* @return string
*/
public function getAESData($aesKey, $dataString)
{
$aes = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_ECB);
$aes->setKey($aesKey);
return $aes->decrypt(urldecode(base64_decode(urldecode($dataString))));
}
/**
* 根据RSA的私钥 解密AES的key
*
* @param string $key
* @return string
*/
public function getDecrypt($key)
{
$rsa = new \phpseclib\Crypt\RSA();
$rsa->loadKey($this->getPrivateKey());
return $rsa->decrypt($key);
}
/**
* RSA根据公钥加密$text
*
* @param string $text
* @return string
*/
public function getEncryptRSA($text)
{
$rsa = new \phpseclib\Crypt\RSA();
$rsa->loadKey($this->getPublicKey());
return $rsa->encrypt($text);
}
/**
* AES加密
*
* @param $key
* @param $text
* @return string
*/
public function getEncryptAES($key, $text)
{
$aes = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_ECB);
$aes->setKey($key);
return $aes->encrypt($text);
}
/**
* 根据公钥加密$text,并base64转码,然后url转码
*
* @param $text
* @return string
*/
public function getEncryptRSAForBase64($text)
{
return urlencode(base64_encode(urlencode($this->getEncryptRSA($text))));
}
public function getEncryptAESForBase64($key, $text)
{
return urlencode(base64_encode(urlencode($this->getEncryptAES($key, $text))));
}
/**
* 创建公钥和私钥
*
* @return array
*/
public function createKey()
{
$rsa = new \phpseclib\Crypt\RSA();
return $rsa->createKey();
}
protected function getPrivateKey()
{
return $this->getConfig()['private'] ?? '';
}
}
<?php
/**
* Created by PhpStorm.
* User: code
* Date: 2019/4/17
* Time: 14:08
*/
namespace App\Helper\Classes\ApiCipher;
use App\Helper\Vendor\Laravel\Cache;
class LaravelRSAAndAES extends BaseRSAAndAES
{
/**
* @return mixed|array
*/
public function getConfig()
{
return config('hashing.rsa');
}
protected function getAESKeyCacheMinute()
{
return config('sys.cache.user-aes-key-minute');
}
/**
* 获取token的缩小版
*
* @return string
*/
protected function getMd5Token()
{
return md5(\Illuminate\Support\Facades\Auth::getToken());
}
/**
* 获取aes的key
*
* @return mixed
*/
public function getUserAESKeyMap()
{
return Cache::get($this->getUserAESKeyToCacheKey(), '');
}
/**
* 保存aes的key
*
* @param $aesKey
*/
public function saveUserAESKeyMap($aesKey)
{
if (empty($aesKey)) {
return;
}
Cache::put($this->getUserAESKeyToCacheKey(), $aesKey, $this->getAESKeyCacheMinute());
}
/**
* 每个接口的AES绑定接口的token
*
* @return string
*/
protected function getUserAESKeyToCacheKey()
{
return 'AES:' . $this->getMd5Token();
}
}
<?php
/**
* Created by PhpStorm.
* User: code
* Date: 2019/4/15
* Time: 11:41
*/
namespace App\Helper\Classes\ApiCipher;
trait RSAAndAESControllerTrait
{
/**
* 保存aes的key,中间件会保存,直接返回成功即可
*
* @return mixed
*/
public function saveAESKey()
{
return $this->success();
}
}
<?php
/**
* Created by PhpStorm.
* User: code
* Date: 2019/4/15
* Time: 11:46
*/
namespace App\Helper\Classes\ApiCipher;
use \App\Helper\Classes\ApiCipher\LaravelRSAAndAES as CipherClass;
class RSAAndAESLaravelInputMiddleware
{
use \Illuminate\Foundation\Validation\ValidatesRequests;
protected $errorText = [
'data_required' => 'data加密的数据不能为空',
'data_error' => 'data加密的数据错误',
'key_error' => 'key密钥错误',
];
/**
* 指定需要加密的AES的key
* 是否加密返回结果
*
* @var string
*/
protected $aesKey = '';
/**
* 处理定义多次的问题
*
* @var bool
*/
protected static $cipherBool = false;
protected function getCipherClass()
{
return CipherClass::instance(); // 系统继承的类
}
/**
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param bool $force 强制校验
* @return mixed
* @throws \Illuminate\Validation\ValidationException
*/
public function handle($request, \Closure $next, $force = false)
{
if (
(!$force && ifNotProduction()) // 只有正式环境才加密
|| !ifFirstBool(__CLASS__)
){
return $next($request);
}
// 中间件结束后执行
\Illuminate\Support\Facades\Event::listen(\Illuminate\Foundation\Http\Events\RequestHandled::class, function ($event){
/** @var \Illuminate\Foundation\Http\Events\RequestHandled $event */
$this->cipherResponse($event->response);
});
// 处理加密
if (empty($this->getRequestKey($request))){ // 已保存AES的key,不需要再传参
$this->handleTokenData($request);
}else{
$this->handleKeyData($request);
}
return $next($request);
}
/**
* 根据token找到AES的key
*
* @param \Illuminate\Http\Request $request
* @throws \Illuminate\Validation\ValidationException
*/
protected function handleTokenData($request)
{
$this->aesKey = $this->getCipherClass()->getUserAESKeyMap();
if (empty($this->aesKey)) {
$this->throwValidateError($request, $this->errorText['key_error']);
}
$requestData = $this->getRequestData($request);
$data = [];
try{
$data = (array)json_decode_not_throw($this->getCipherClass()->getAESData($this->aesKey, $requestData['data']));
}catch (\Exception $exception){ // 处理Decryption error报错
$this->throwValidateError($request, $exception->getMessage());
}
$this->requestSet($request, $data);
}
/**
* 每次都传key
*
* @param \Illuminate\Http\Request $request
* @throws \Illuminate\Validation\ValidationException
*/
protected function handleKeyData($request)
{
$requestKey = $this->getRequestKey($request);
$requestData = $this->getRequestData($request);
$data = '';
try{
$this->aesKey = $this->getCipherClass()->getRSADecrypt($requestKey);
$data = !empty($requestData['data']) ? $this->getCipherClass()->getAESData($this->aesKey, $requestData['data']) : '';
}catch (\Exception $exception){ // 处理Decryption error报错
$this->throwValidateError($request, $exception->getMessage());
}
if ($data !== false){
$data = json_decode_not_throw($data);
}else{ // 解密失败,如果是空则返回空字符串
$this->throwValidateError($request, $this->errorText['data_error']);
}
$this->requestSet($request, (array)$data);
$this->getCipherClass()->saveUserAESKeyMap($this->aesKey);
}
/**
* 获取请求参数里的AES的key的字符串
*
* @param \Illuminate\Http\Request $request
* @return array|string|null
*/
protected function getRequestKey($request)
{
return $request->header('aes-key');
}
/**
* 获取请求参数里的被AES加密的数据
*
* @param \Illuminate\Http\Request $request
* @return array
* @throws \Illuminate\Validation\ValidationException
*/
protected function getRequestData($request)
{
$requestData = $this->validate($request, [
'data' => 'sometimes|required'
], ['data.required' => $this->errorText['data_required'],]);
$request->offsetUnset('data');
$requestData['data'] = $requestData['data'] ?? '';
return $requestData;
}
/**
* 抛出验证错误
*
* @param \Illuminate\Http\Request $request
* @param $message
* @throws \Illuminate\Validation\ValidationException
*/
protected function throwValidateError($request, $message)
{
$request->merge(['aesKey' => $this->aesKey]); // 通过request来传参
$this->validate(\Illuminate\Http\Request::create(''), ['key' => 'required'], ['key.required' => $message]);
}
/**
* @param \Illuminate\Http\Request $request
* @param $data
*/
protected function requestSet($request, $data)
{
foreach ($request->keys() as $key) {
unset($request[$key]);
}
$request->merge($data);
}
/**
* 加密response
*
* @param $dataResponse
*/
protected function cipherResponse($dataResponse)
{
if ($this->aesKey){ // 需要加密返回的response
$aesKey = $this->aesKey;
$this->aesKey = '';
if (is_object($dataResponse) && $dataResponse instanceof \Illuminate\Http\JsonResponse){ // 全加密
// 请求要考虑get,所以请求时加密的数据要放在
$dataResponse->setContent($this->getCipherClass()->getEncryptAES($aesKey, $dataResponse->getContent()));
}
}
}
}
以上是关于PHP 加密:AES & RSA的主要内容,如果未能解决你的问题,请参考以下文章
基于RSA+AES实现前后端(VUE+PHP)参数加密传输
php aes和rsa加密的区别
java加密用PHP解密
PHP如何实现AES加解密
RSA+AES请求组合加密
前端AES + RSA加密