PHP OpenSSL擴展 - 對稱加密

PHP 在進入7.x 時代後,默認就不再附帶 mcrypt 擴展,mcrypt 將被 openssl_* 一族函數所替代。所以,對於 PHPer 來說,有必要學習一下 PHP 的 OpenSSL 擴展。

本文就先從 OpenSSL 擴展中的對稱加密說起。後面會陸續更多非對稱加密、數字簽名、數字證書等函數的講解。

PHP 的 OpenSSL 擴展中,對稱加密的相關函數有:

  • openssl_encrypt()
  • openssl_decrypt()
  • openssl_random_pseudo_bytes()
  • openssl_get_cipher_methods()
  • openssl_cipher_iv_length()

光看PHP的官方文檔還有點難理解。上一段代碼,更清楚地看下這些函數怎麼完成加密的:

 

// 加密算法
1. $encryptMethod = 'aes-256-cbc';
// 明文數據
2. $data = 'Hello World';

// 生成IV
3. $ivLength = openssl_cipher_iv_length($encryptMethod);
4. $iv = openssl_random_pseudo_bytes($ivLength, $isStrong);
5. if (false === $iv && false === $isStrong) {
6.     die('IV generate failed');
7. }

// 加密
8. $encrypted = openssl_encrypt($data, $encryptMethod, 'secret', 0, $iv);
// 解密
9. $decrypted = openssl_decrypt($encrypted, $encryptMethod, 'secret', 0, $iv);

詳細解釋一下:

第 1 行 指定了加密算法。比如這段代碼使用 aes-256-cbc 算法加密。其實PHP的OpenSSL擴展支持很多種加密算法,想知道所有對稱加密算法名稱列表,可以調用 openssl_get_cipher_methods() 函數,這會返回一個數組:

 

array(
  0 => 'AES-128-CBC',
  1 => 'AES-128-CBC-HMAC-SHA1',
  ...
  7 => 'AES-128-ECB',
  ...
  31 => 'BF-CBC',
  200 => 'seed-ofb',
)

你會發現函數返回將近200種加密算法,實際上沒有這麼多,許多隻是因爲大小寫不同而重複了,比如 AES-128-CBCaes-128-cbc 實際上是同一種加密算法。如果去掉重複項,那麼 PHP 的 OpenSSL 擴展支持大概100多種不同的加密算法。

第 3 ~ 7 行 生成了 IV。爲什麼要生成 IV,這個 IV 有什麼用?

回顧一下 openssl_get_cipher_methods() 返回的加密算法列表,有很多名字中間帶有 “CBC” 字樣,這些加密算法使用了同一種加密模式,也就是 密碼分組鏈接模式(Cipher Block Chaining)

CBC 模式的加密算法中,明文會被分成若干個組,以組爲單位加密。每個組的加密過程,依賴他前一個組的數據:需要跟前一組的數據進行異或操作後生成本組的密文。那麼最開頭的那個組又要依賴誰呢?依賴的就是 IV,所以這就是爲什麼 IV 要叫初始化向量。IV 是 初始化向量(initialization vector)的縮寫

IV 應該是隨機生成的,所以代碼用到了 openssl_random_pseudo_bytes() 生成 IV。該函數接收一個 int,代表需要生成的 IV 的長度。

IV 長度隨加密算法不同而不同。一般人是記不住那麼多算法需要的 IV 長度的。所以直接使用 openssl_cipher_iv_length() 函數,這個函數返回一個 int,表示加密算法需要的 IV 長度:

 

echo openssl_cipher_iv_length('AES-256-CBC'); // 16
echo openssl_cipher_iv_length('BC-CBC'); // 8
echo openssl_cipher_iv_length('AES-128-ECB'); // 0

比如 AES-256-CBC 需要16位的 IV、 BC-CBC 需要 8 位的 IV、而AES-128-ECB 不需要 IV,所以返回了 0。

第 8 ~ 9 行 是加密和解密。分別使用了 openssl_encrypt()openssl_decrypt()

  • 第一個參數是輸入,對 openssl_encrypt() 來說是明文串,對 openssl_decrypt() 來說是密文串
  • 第二個參數是指定加密 / 解密 算法
  • 第三個參數是加密 / 解密時需要用到的密碼,是個字符串
  • 第四個參數額外選項,沒有特殊需要可以保持默認值:0,
  • 第五個參數是 IV

這兩個函數除了第一個參數不同,其餘參數都要保證相同才能順利解密。最後,在使用需要 IV 的加密算法時,需要注意:

  • 必須傳 $iv 參數,不傳的話PHP將會拋出一個 Warning
  • IV 應該是隨機生成的(比如用 openssl_random_pseudo_bytes() ),不能人爲設定
  • 每次加密都應該重新生成一次 IV ,不可偷懶多次加密採用相同 IV
  • IV 要隨着密文一起保存(不然就沒法解密了啦),可以直接附在密文串後面,也可以分開保存

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章