這篇文章主要給大家介紹了關於Laravel中encrypt和decrypt的實現方法,文中通過範例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
前言
Laravel 的加密機制使用 OpenSSL 提供 AES-256 和 AES-128 的加密,本文將詳細介紹關於Laravel中encrypt和decrypt的實現,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。
1. 使用方法
首先是產生秘鑰。要需要在.env目錄裡提供APP_KEY,這個如果沒有的話,可以通過命令php artisan key:generate
產生,也可以自己設定。產生後例子應該是這樣的
APP_KEY=base64:5BM1BXGOBrGeeqJMAWJZSzyzh5yPcCGOcOGPtUij65g=
在檔案配置加密key和密碼編譯演算法,在config/app.php的目錄裡有配置
$ 'key' => env('APP_KEY'), 'cipher' => 'AES-256-CBC',
使用方法,在laravel裡已經有使用方法了,這裡就不在過多的說了。主要使用的兩個方法,一個是encrypt的加密,一個是decrypt的解密
2. 尋找加密解密的檔案
實現方法的位置是在vendor/illuminate/encryption/的目錄下發現兩個檔案,一個是EncryptionServiceProvider另外一個是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裡面有沒有帶'base64' $key = base64_decode(substr($key, 7)); //如果有的話,把key前面的base64:給取消,並且解析出原來的字串 } return new Encrypter($key, $config['cipher']); //執行個體化Encrypte類,注入到架構裡 }); }
這個檔案沒太多東西,但是通過這個我們可以看出,其實在設定檔的,我們能直接寫key,並且前面不帶base64也是可以解析。相當於省幾步操作
另外,在執行個體化類的時候,需要傳入key以及加密方式
4. 分析Encrypter檔案
1. 分析__construct,在執行個體化之前執行
public function __construct($key, $cipher = 'AES-128-CBC') { $key = (string) $key; //把key轉換為字串 if (static::supported($key, $cipher)) { //調用一個自訂的方法,用來判斷加密方式和要求的key長度是否一樣 $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.'); } }
上面的方法,主要是用來判斷加密方式和傳的key的長度是否相同,因為不同的加密方式,要求的相應的key的長度也是有要求的,具體每種加密方式要求key的長度可以尋找對應的文檔
public static function supported($key, $cipher) { $length = mb_strlen($key, '8bit'); //判斷key的字元的長度,按照8bit位的方式計算字元長度 return ($cipher === 'AES-128-CBC' && $length === 16) || ($cipher === 'AES-256-CBC' && $length === 32); //編碼格式為AES128的要求字元長度為16。編碼格式為AES256的要求字元長度為32位 }
上面這個方法展現了一個嚴謹的地方,用了mb_strlen方法,並且要求計算長度是按照8bit位來計算的。這樣的好處是,不管是在哪種作業系統,計算的長度都是一樣的。
通過這個考慮到不同作業系統的情況,不會出現加密出現問題的情況。
2. 分析encrypt方法
public function encrypt($value, $serialize = true) { $iv = random_bytes(16); //產生一個16位的隨機字串 // 使用openssl_encrypt把資料產生一個加密的資料 // 1、判斷需要不需要產生一個可儲存表示的值,這樣做是為了不管你的資料是數組還是字串都能給你轉成一個字串,不至於在判斷你傳過來的資料是數組還是字串了。 // 2、使用openssl_encrypt。第一個參數是傳入資料,第二個參數是傳入加密方式,目前使用AES-256-CBC的加密方式,第三個參數是,返回加密後的未經處理資料,還是把加密的資料在經過一次base64的編碼,0的話表示base64位元據。第四個參數是項量,這個參數傳入隨機數,是為了在加密資料的時候每次的加密資料都不一樣。 $value = \openssl_encrypt( $serialize ? serialize($value) : $value, $this->cipher, $this->key, 0, $iv ); //使用AES256加密內容 if ($value === false) { throw new EncryptException('Could not encrypt the data.'); } $mac = $this->hash($iv = base64_encode($iv), $value); //產生一個簽名,用來保證內容參數沒有被更改 $json = json_encode(compact('iv', 'value', 'mac')); //把隨機碼,加密內容,已經簽名,組成數組,並轉成json格式 if (! is_string($json)) { throw new EncryptException('Could not encrypt the data.'); } return base64_encode($json); //把json格式轉換為base64位,用於傳輸 }
上面用到了一個自訂的方法hash(),我們可以看下方法的實現。
protected function hash($iv, $value) { // 產生簽名 // 1、把隨機值轉為base64 // 2、使用hash_hmac產生sha256的加密值,用來驗證參數是否更改。第一個參數表示加密方式,目前是使用sha256,第二個是用隨機值連上加密過後的內容進行,第三個參數是上步使用的key。產生簽名。 return hash_hmac('sha256', $iv.$value, $this->key); /根據隨機值和內容,產生一個sha256的簽名 }
以上加密共分了三大步
1、產生隨機碼
2、產生加密內容
3、產生簽名
架構用到一個優雅的方法,使用serialize產生一個值,這個方法高雅在哪裡,就是不管你得內容是數組還是字串,都能轉換成字串。 而使用serialize和使用json_encode的區別在哪,我想最大的好處是,你所要加密的內容比較大的時候,serialize相對於要快。
另外一個地方是,架構在加密的時候使用了一個隨機字串。為什麼要使用隨機字串呢,因為使用了隨機字串,使每次加密的內容都是不一樣的,防止別人猜出來。
3. 分析decrypt方法
解密資料,可以說是最複雜的一塊,不僅要進行資料的解密,而且還要保證資料的完整性,以及資料防篡改
public function decrypt($payload, $unserialize = true) { $payload = $this->getJsonPayload($payload); //把加密後的字串轉換出成數組。 $iv = base64_decode($payload['iv']); //把隨機字串進行base64解密出來 $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; //把資料轉換為未經處理資料 }
getJsonPayload方法
protected function getJsonPayload($payload) { $payload = json_decode(base64_decode($payload), true); //把資料轉換為原來的數組形式 if (! $this->validPayload($payload)) { //驗證是不是數組以及數組裡有沒有隨機字串,加密後的內容,簽名 throw new DecryptException('The payload is invalid.'); } if (! $this->validMac($payload)) { //驗證資料是否被篡改 throw new DecryptException('The MAC is invalid.'); } return $payload; }
validPayload方法就不說了,比較簡單和基本,重點就說說validMac驗證這塊,保證資料不被篡改,這是最重要的
protected function validMac(array $payload) { $calculated = $this->calculateMac($payload, $bytes = random_bytes(16)); //拿資料和隨機值產生一個簽名 return hash_equals( //比對上一步產生的簽名和下面產生的簽名的hash是否一樣。 hash_hmac('sha256', $payload['mac'], $bytes, true), $calculated //根據未經處理資料裡的簽名在新產生一個簽名 ); }
calculateMac方法是為了根據未經處理資料和隨機值產生一個簽名,然後用這簽名再次產生一個簽名
protected function calculateMac($payload, $bytes) { return hash_hmac( 'sha256', $this->hash($payload['iv'], $payload['value']), $bytes, true ); }
以上解密共分了三大步
1、判斷資料的完整性
2、判斷資料的一致性
3、解密資料內容。
這個驗證簽名有個奇怪的地方,他並不像我們平常驗證簽名一樣。我們平常驗證簽名都是,拿未經處理資料和隨機值產生一個簽名,然後拿產生的簽名和未經處理資料的簽名進行比對來判斷是否有被篡改。
而架構卻多了一個,他用的是,通過未經處理資料和隨機值產生簽名後,又拿這個簽名產生了一個簽名,而要比對的也是拿未經處理資料裡的簽名在產生一個簽名,然後進行比對。目前想不出,為什麼要多幾步操作。
在加密的時候,我們把未經處理資料使用serialize轉換了一下,所以我們相應的也需要使用unserialize把資料轉換回來。
注意
加密時使用的openssl_encrypt裡的隨機項量值是使用的未經處理資料raw這種二進位的值,使用openssl_decrypt解密後的值是使用的經過base64位後的隨機字串。
解密的時候產生簽名比較的時候,不是用原來的簽名,然後根據未經處理資料的內容,重建一次簽名進行比較,而是使用原始簽名為基礎產生一個簽名,然後在拿未經處理資料為基礎產生的簽名,在用這個新產生的簽名重建了一次簽名。然後進行比較的。
AES256是加密資料,後面能夠逆向在進行解密出資料。而SHA256是產生簽名的,這個過程是無法復原的,是為了驗證資料的完整性。