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"
}
关键代码:
this−>validMac(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);}
数字签名:
验签: