這篇文章主要介紹了Laravel中如何實(shí)現(xiàn)encrypt和decrypt,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
在白沙黎族等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、成都做網(wǎng)站 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計(jì),全網(wǎng)整合營(yíng)銷推廣,外貿(mào)網(wǎng)站制作,白沙黎族網(wǎng)站建設(shè)費(fèi)用合理。1. 使用方法
首先是生成秘鑰。要需要在.env目錄里提供APP_KEY,這個(gè)如果沒(méi)有的話,可以通過(guò)命令php artisan key:generate
生成,也可以自己設(shè)置。生成后例子應(yīng)該是這樣的
APP_KEY=base64:5BM1BXGOBrGeeqJMAWJZSzyzh6yPcCGOcOGPtUij65g=
在文件配置加密key和加密算法,在config/app.php的目錄里有配置
$ 'key' => env('APP_KEY'), 'cipher' => 'AES-256-CBC',
使用方法,在laravel里已經(jīng)有使用方法了,這里就不在過(guò)多的說(shuō)了。主要使用的兩個(gè)方法,一個(gè)是encrypt的加密,一個(gè)是decrypt的解密
2. 查找加密解密的文件
實(shí)現(xiàn)方法的位置是在vendor/illuminate/encryption/的目錄下發(fā)現(xiàn)兩個(gè)文件,一個(gè)是EncryptionServiceProvider另外一個(gè)是Encrypter
3. 分析EncryptionServiceProvider文件
public function register() { $this->app->singleton('encrypter', function ($app) { $config = $app->make('config')->get('app'); //從config/app.php里拿到配置文件 if (Str::startsWith($key = $config['key'], 'base64:')) { //分析配置文件里的key里面有沒(méi)有帶'base64' $key = base64_decode(substr($key, 7)); //如果有的話,把key前面的base64:給取消,并且解析出原來(lái)的字符串 } return new Encrypter($key, $config['cipher']); //實(shí)例化Encrypte類,注入到框架里 }); }
這個(gè)文件沒(méi)太多東西,但是通過(guò)這個(gè)我們可以看出,其實(shí)在配置文件的,我們能直接寫(xiě)key,并且前面不帶base64也是可以解析。相當(dāng)于省幾步操作
另外,在實(shí)例化類的時(shí)候,需要傳入key以及加密方式
4. 分析Encrypter文件
1. 分析__construct,在實(shí)例化之前執(zhí)行
public function __construct($key, $cipher = 'AES-128-CBC') { $key = (string) $key; //把key轉(zhuǎn)換為字符串 if (static::supported($key, $cipher)) { //調(diào)用一個(gè)自定義的方法,用來(lái)判斷加密方式和要求的key長(zhǎng)度是否一樣 $this->key = $key; $this->cipher = $cipher; } else { throw new RuntimeException('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.'); } }
上面的方法,主要是用來(lái)判斷加密方式和傳的key的長(zhǎng)度是否相同,因?yàn)椴煌募用芊绞?,要求的相?yīng)的key的長(zhǎng)度也是有要求的,具體每種加密方式要求key的長(zhǎng)度可以查找對(duì)應(yīng)的文檔
public static function supported($key, $cipher) { $length = mb_strlen($key, '8bit'); //判斷key的字符的長(zhǎng)度,按照8bit位的方式計(jì)算字符長(zhǎng)度 return ($cipher === 'AES-128-CBC' && $length === 16) || ($cipher === 'AES-256-CBC' && $length === 32); //編碼格式為AES128的要求字符長(zhǎng)度為16。編碼格式為AES256的要求字符長(zhǎng)度為32位 }
上面這個(gè)方法展現(xiàn)了一個(gè)嚴(yán)謹(jǐn)?shù)牡胤?,用了mb_strlen方法,并且要求計(jì)算長(zhǎng)度是按照8bit位來(lái)計(jì)算的。這樣的好處是,不管是在哪種操作系統(tǒng),計(jì)算的長(zhǎng)度都是一樣的。
通過(guò)這個(gè)考慮到不同操作系統(tǒng)的情況,不會(huì)出現(xiàn)加密出現(xiàn)問(wèn)題的情況。
2. 分析encrypt方法
public function encrypt($value, $serialize = true) { $iv = random_bytes(16); //生成一個(gè)16位的隨機(jī)字符串 // 使用openssl_encrypt把數(shù)據(jù)生成一個(gè)加密的數(shù)據(jù) // 1、判斷需要不需要生成一個(gè)可存儲(chǔ)表示的值,這樣做是為了不管你的數(shù)據(jù)是數(shù)組還是字符串都能給你轉(zhuǎn)成一個(gè)字符串,不至于在判斷你傳過(guò)來(lái)的數(shù)據(jù)是數(shù)組還是字符串了。 // 2、使用openssl_encrypt。第一個(gè)參數(shù)是傳入數(shù)據(jù),第二個(gè)參數(shù)是傳入加密方式,目前使用AES-256-CBC的加密方式,第三個(gè)參數(shù)是,返回加密后的原始數(shù)據(jù),還是把加密的數(shù)據(jù)在經(jīng)過(guò)一次base64的編碼,0的話表示base64位數(shù)據(jù)。第四個(gè)參數(shù)是項(xiàng)量,這個(gè)參數(shù)傳入隨機(jī)數(shù),是為了在加密數(shù)據(jù)的時(shí)候每次的加密數(shù)據(jù)都不一樣。 $value = \openssl_encrypt( $serialize ? serialize($value) : $value, $this->cipher, $this->key, 0, $iv ); //使用AES256加密內(nèi)容 if ($value === false) { throw new EncryptException('Could not encrypt the data.'); } $mac = $this->hash($iv = base64_encode($iv), $value); //生成一個(gè)簽名,用來(lái)保證內(nèi)容參數(shù)沒(méi)有被更改 $json = json_encode(compact('iv', 'value', 'mac')); //把隨機(jī)碼,加密內(nèi)容,已經(jīng)簽名,組成數(shù)組,并轉(zhuǎn)成json格式 if (! is_string($json)) { throw new EncryptException('Could not encrypt the data.'); } return base64_encode($json); //把json格式轉(zhuǎn)換為base64位,用于傳輸 }
上面用到了一個(gè)自定義的方法hash(),我們可以看下方法的實(shí)現(xiàn)。
protected function hash($iv, $value) { // 生成簽名 // 1、把隨機(jī)值轉(zhuǎn)為base64 // 2、使用hash_hmac生成sha256的加密值,用來(lái)驗(yàn)證參數(shù)是否更改。第一個(gè)參數(shù)表示加密方式,目前是使用sha256,第二個(gè)是用隨機(jī)值連上加密過(guò)后的內(nèi)容進(jìn)行,第三個(gè)參數(shù)是上步使用的key。生成簽名。 return hash_hmac('sha256', $iv.$value, $this->key); /根據(jù)隨機(jī)值和內(nèi)容,生成一個(gè)sha256的簽名 }
以上加密共分了三大步
1、生成隨機(jī)碼
2、生成加密內(nèi)容
3、生成簽名
框架用到一個(gè)優(yōu)雅的方法,使用serialize生成一個(gè)值,這個(gè)方法高雅在哪里,就是不管你得內(nèi)容是數(shù)組還是字符串,都能轉(zhuǎn)換成字符串。 而使用serialize和使用json_encode的區(qū)別在哪,我想較大的好處是,你所要加密的內(nèi)容比較大的時(shí)候,serialize相對(duì)于要快。
另外一個(gè)地方是,框架在加密的時(shí)候使用了一個(gè)隨機(jī)字符串。為什么要使用隨機(jī)字符串呢,因?yàn)槭褂昧穗S機(jī)字符串,使每次加密的內(nèi)容都是不一樣的,防止別人猜出來(lái)。
3. 分析decrypt方法
解密數(shù)據(jù),可以說(shuō)是最復(fù)雜的一塊,不僅要進(jìn)行數(shù)據(jù)的解密,而且還要保證數(shù)據(jù)的完整性,以及數(shù)據(jù)防篡改
public function decrypt($payload, $unserialize = true) { $payload = $this->getJsonPayload($payload); //把加密后的字符串轉(zhuǎn)換出成數(shù)組。 $iv = base64_decode($payload['iv']); //把隨機(jī)字符串進(jìn)行base64解密出來(lái) $decrypted = \openssl_decrypt( //解密數(shù)據(jù) $payload['value'], $this->cipher, $this->key, 0, $iv ); if ($decrypted === false) { throw new DecryptException('Could not decrypt the data.'); } return $unserialize ? unserialize($decrypted) : $decrypted; //把數(shù)據(jù)轉(zhuǎn)換為原始數(shù)據(jù) }
getJsonPayload方法
protected function getJsonPayload($payload) { $payload = json_decode(base64_decode($payload), true); //把數(shù)據(jù)轉(zhuǎn)換為原來(lái)的數(shù)組形式 if (! $this->validPayload($payload)) { //驗(yàn)證是不是數(shù)組以及數(shù)組里有沒(méi)有隨機(jī)字符串,加密后的內(nèi)容,簽名 throw new DecryptException('The payload is invalid.'); } if (! $this->validMac($payload)) { //驗(yàn)證數(shù)據(jù)是否被篡改 throw new DecryptException('The MAC is invalid.'); } return $payload; }
validPayload方法就不說(shuō)了,比較簡(jiǎn)單和基本,重點(diǎn)就說(shuō)說(shuō)validMac驗(yàn)證這塊,保證數(shù)據(jù)不被篡改,這是最重要的
protected function validMac(array $payload) { $calculated = $this->calculateMac($payload, $bytes = random_bytes(16)); //拿數(shù)據(jù)和隨機(jī)值生成一個(gè)簽名 return hash_equals( //比對(duì)上一步生成的簽名和下面生成的簽名的hash是否一樣。 hash_hmac('sha256', $payload['mac'], $bytes, true), $calculated //根據(jù)原始數(shù)據(jù)里的簽名在新生成一個(gè)簽名 ); }
calculateMac方法是為了根據(jù)原始數(shù)據(jù)和隨機(jī)值生成一個(gè)簽名,然后用這簽名再次生成一個(gè)簽名
protected function calculateMac($payload, $bytes) { return hash_hmac( 'sha256', $this->hash($payload['iv'], $payload['value']), $bytes, true ); }
以上解密共分了三大步
1、判斷數(shù)據(jù)的完整性
2、判斷數(shù)據(jù)的一致性
3、解密數(shù)據(jù)內(nèi)容。
這個(gè)驗(yàn)證簽名有個(gè)奇怪的地方,他并不像我們平常驗(yàn)證簽名一樣。我們平常驗(yàn)證簽名都是,拿原始數(shù)據(jù)和隨機(jī)值生成一個(gè)簽名,然后拿生成的簽名和原始數(shù)據(jù)的簽名進(jìn)行比對(duì)來(lái)判斷是否有被篡改。
而框架卻多了一個(gè),他用的是,通過(guò)原始數(shù)據(jù)和隨機(jī)值生成簽名后,又拿這個(gè)簽名生成了一個(gè)簽名,而要比對(duì)的也是拿原始數(shù)據(jù)里的簽名在生成一個(gè)簽名,然后進(jìn)行比對(duì)。目前想不出,為什么要多幾步操作。
在加密的時(shí)候,我們把原始數(shù)據(jù)使用serialize轉(zhuǎn)換了一下,所以我們相應(yīng)的也需要使用unserialize把數(shù)據(jù)轉(zhuǎn)換回來(lái)。
注意
加密時(shí)使用的openssl_encrypt里的隨機(jī)項(xiàng)量值是使用的原始數(shù)據(jù)raw這種二進(jìn)制的值,使用openssl_decrypt解密后的值是使用的經(jīng)過(guò)base64位后的隨機(jī)字符串。
解密的時(shí)候生成簽名比較的時(shí)候,不是用原來(lái)的簽名,然后根據(jù)原始數(shù)據(jù)的內(nèi)容,重新生成一次簽名進(jìn)行比較,而是使用原始簽名為基礎(chǔ)生成一個(gè)簽名,然后在拿原始數(shù)據(jù)為基礎(chǔ)生成的簽名,在用這個(gè)新生成的簽名重新生成了一次簽名。然后進(jìn)行比較的。
AES256是加密數(shù)據(jù),后面能夠逆向在進(jìn)行解密出數(shù)據(jù)。而SHA256是生成簽名的,這個(gè)過(guò)程是不可逆的,是為了驗(yàn)證數(shù)據(jù)的完整性。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Laravel中如何實(shí)現(xiàn)encrypt和decrypt”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!