php RSA和AES的加密

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 RSA和AES的加密的主要内容,如果未能解决你的问题,请参考以下文章

php aes和rsa加密的区别

基于RSA+AES实现前后端(VUE+PHP)参数加密传输

PHP如何实现AES加解密

java加密用PHP解密

RSA+AES请求组合加密

RSA非对称加密和AES对称加密