首頁  >  文章  >  後端開發  >  Laravel中encrypt和decrypt實作方法實例分享

Laravel中encrypt和decrypt實作方法實例分享

小云云
小云云原創
2018-01-10 16:57:491800瀏覽

本文主要為大家介紹了關於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是產生簽章的,這個過程是不可逆的,是為了驗證資料的完整性。

相關推薦:

MySQL如何正確地利用AES_ENCRYPT()與AES_DECRYPT()加解密

#使用laravel 5.1出現No supported encrypter found錯誤的解決方法

javascript - 小程式wx.getUserInfo 中的 encryptData 解密

#

以上是Laravel中encrypt和decrypt實作方法實例分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn