cookie加密解密和保证数据完整性(不被篡改)

cookie加密解密和保证数据完整性

AES-128-CBC`加密算法

AES-128-CBC是一种分组对称加密算法,即用同一组key进行明文和密文的转换,以128bit为一组,128bit==16Byte,意思就是明文的16字节为一组对应加密后的16字节的密文。
若最后剩余的明文不够16字节,需要进行填充,通常采用PKCS7进行填充。比如最后缺3个字节,则填充3个字节的0x03;若最后缺10个字节,则填充10个字节的0x0a;
若明文正好是16个字节的整数倍,最后要再加入一个16字节0x10的组再进行加密

CBC加密原理:
明文跟向量异或,再用KEY进行加密,结果作为下个BLOCK的初始化向量。

解密原理:
使用密钥先对密文解密,解密后再同初始向量异或得到明文。

加密时需要的参数:
1、传入要加密的明文
2、传入一个16字节的key
3、传入一个16字节的初始偏移向量IV

解密时需要的参数:

1、带解密的密文
2、加密解密的key一致
3、跟加密时传递IV参数一致

正文:

laravel初始化会加载 \App\Http\Middleware\EncryptCookies::class中间件用来加密解密cookie
代码:


use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected $except = [
        //
    ];
}

EncryptCookies 继承了 EncryptCookies,处理逻辑都在EncryptCookies这个类中

关键代码:

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function handle($request, Closure $next)
    {
        return $this->encrypt($next($this->decrypt($request))); //加密解密
    }

加密代码:

    /**
     * Encrypt the given value.
     *
     * @param  mixed  $value
     * @param  bool  $serialize
     * @return string
     *
     * @throws \Illuminate\Contracts\Encryption\EncryptException
     */
    public function encrypt($value, $serialize = true)
    {
        $iv = random_bytes(openssl_cipher_iv_length($this->cipher));//偏移向量IV


        // First we will encrypt the value using OpenSSL. After this is encrypted we
        // will proceed to calculating a MAC for the encrypted value so that this
        // value can be verified later as not having been changed by the users.
        $value = \openssl_encrypt(
            $serialize ? serialize($value) : $value,
            $this->cipher, $this->key, 0, $iv
        );//key在.env中配置

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        // Once we get the encrypted value we'll go ahead and base64_encode the input
        // vector and create the MAC for the encrypted value so we can then verify
        // its authenticity. Then, we'll JSON the data into the "payload" array.
        $mac = $this->hash($iv = base64_encode($iv), $value); //签名,base64加密之后再计算,双重加密

        $json = json_encode(compact('iv', 'value', 'mac'));
  

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);//再次加密
    }

这里的$this->encrypter对应是vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php类

  /**
     * Encrypt the given value.
     *
     * @param  mixed  $value
     * @param  bool  $serialize
     * @return string
     *
     * @throws \Illuminate\Contracts\Encryption\EncryptException
     */
    public function encrypt($value, $serialize = true)
    {
        $iv = random_bytes(openssl_cipher_iv_length($this->cipher));

        // First we will encrypt the value using OpenSSL. After this is encrypted we
        // will proceed to calculating a MAC for the encrypted value so that this
        // value can be verified later as not having been changed by the users.
        $value = \openssl_encrypt(
            $serialize ? serialize($value) : $value,
            $this->cipher, $this->key, 0, $iv
        );

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        // Once we get the encrypted value we'll go ahead and base64_encode the input
        // vector and create the MAC for the encrypted value so we can then verify
        // its authenticity. Then, we'll JSON the data into the "payload" array.
        $mac = $this->hash($iv = base64_encode($iv), $value);

        $json = json_encode(compact('iv', 'value', 'mac'));

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);
    }

Laravel 的加密机制使用的是 OpenSSL 所提供的 AES-256 和 AES-128 加密,所有 Laravel 加密之后的结果都会使用消息认证码 (MAC) 签名,使其底层值不能在加密后再次修改
采用的是AES-128-CBC加密算法

解密代码:

  /**
     * Decrypt the given value.
     *
     * @param  string  $payload
     * @param  bool  $unserialize
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Encryption\DecryptException
     */
    public function decrypt($payload, $unserialize = true)
    {
        $payload = $this->getJsonPayload($payload);

        $iv = base64_decode($payload['iv']);

        // Here we will decrypt the value. If we are able to successfully decrypt it
        // we will then unserialize it and return it out to the caller. If we are
        // unable to decrypt this value we will throw out an exception message.
        $decrypted = \openssl_decrypt(
            $payload['value'], $this->cipher, $this->key, 0, $iv
        );

        if ($decrypted === false) {
            throw new DecryptException('Could not decrypt the data.');
        }

        return $unserialize ? unserialize($decrypted) : $decrypted;
    }
   /**
     * Get the JSON array from the given payload.
     *
     * @param  string  $payload
     * @return array
     *
     * @throws \Illuminate\Contracts\Encryption\DecryptException
     */
    protected function getJsonPayload($payload)
    {
        $payload = json_decode(base64_decode($payload), true); //之前加密这里对应解密
        var_dump($payload);

        // If the payload is not valid JSON or does not have the proper keys set we will
        // assume it is invalid and bail out of the routine since we will not be able
        // to decrypt the given value. We'll also check the MAC for this encryption.
        if (! $this->validPayload($payload)) {
            throw new DecryptException('The payload is invalid.');
        }

        if (! $this->validMac($payload)) {
            throw new DecryptException('The MAC is invalid.');
        }

        return $payload;
    }
array(3) {
  ["iv"]=>
  string(24) "YlBI1gpkduvfhdVbKtVI0g=="
  ["value"]=>
  string(64) "kdL5N3LkUNoCtsHFcQAWmBx4+3+pCH6814uV0l4Y3A4qMhxr2/ZsT9W/VGXU21yB"
  ["mac"]=>
  string(64) "4d47c1452cf3b8772e6528dae7247a51a9b95e36636b9228ab4f081522a55b8e"
}

关键代码:
t h i s − > v a l i d M a c ( this->validMac( this>validMac(payload)方法验证数据完整性

    /**
     * Determine if the MAC for the given payload is valid.
     *
     * @param  array  $payload
     * @return bool
     */
    protected function validMac(array $payload)
    {
        $calculated = $this->calculateMac($payload, $bytes = random_bytes(16));同样算法算出签名

        return hash_equals( 
            hash_hmac('sha256', $payload['mac'], $bytes, true), $calculated
        );//和之前存储的签名作比较
    }
    /**
     * Calculate the hash of the given payload.
     *
     * @param  array  $payload
     * @param  string  $bytes
     * @return string
     */
    protected function calculateMac($payload, $bytes)
    {
        return hash_hmac(
            'sha256', $this->hash($payload['iv'], $payload['value']), $bytes, true
        );
    }

扩展

数字签名:
在这里插入图片描述
验签:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值