CodeIgniter 加密類(新版)

2018-07-21 15:39 更新

加密類(新版)

重要

絕不要使用這個類或其他任何加密類來進行密碼處理!密碼應該是被哈希, 你應該使用 PHP 自帶的 密碼哈希擴展 。

加密類提供了雙向數據加密的方式,為了實現密碼學意義上的安全,它使用了一些并非在所有系統上都可用的 PHP 的擴展, 要使用這個類,你的系統上必須安裝了下面的擴展:

  • OpenSSL (以及 PHP 5.3.3)
  • MCrypt (要支持 MCRYPT_DEV_URANDOM)

只要有一點不滿足,我們就無法為你提供足夠高的安全性。

使用加密類

初始化類

正如 CodeIgniter 中的其他類一樣,在你的控制器中使用 $this->load->library() 方法來初始化加密類:

$this->load->library('encryption');

初始化之后,加密類的對象就可以這樣訪問:

$this->encryption

默認行為

默認情況下,加密類會通過你配置的 encryption_key 參數和 SHA512 HMAC 認證, 使用 AES-128 算法的 CBC 模式。

注解

選擇使用 AES-128 算法不僅是因為它已經被證明相當強壯, 而且它也已經在不同的加密軟件和編程語言 API 中廣泛的使用了。

但是要注意的是,encryption_key 參數的用法可能并不是你想的那樣。

如果你對密碼學有點熟悉的話,你應該知道,使用 HMAC 算法認證也需要使用一個密鑰, 而在加密的過程和認證的過程中使用相同的密鑰可不是個好的做法。

正因為此,程序會從你的配置的 encryption_key 參數中派生出兩個密鑰來: 一個用于加密,另一個用于認證。這其實是通過一種叫做 HKDF (HMAC-based Key Derivation Function)的技術實現的。

設置 encryption_key 參數

加密密鑰( encryption key )是用于控制加密過程的一小段信息,使用它可以對普通文本進行加密和解密。

這個過程可以保證只有你能對數據進行解密,其他人是看不到你的數據的,這其中的關鍵就是 加密密鑰。 如果你使用了一個密鑰來加密數據,那么就只能通過這個密鑰來解密,所以你不僅應該仔細選擇你的密鑰, 還應該好好的保管好它,不要忘記了。

還有一點要注意的是,為了確保最高的安全性,這個密鑰不僅 應該 越強壯越好,而且 應該 經常修改。 不過這在現實中很難做到,也不好實現,所以 CodeIgniter 提供了一個配置參數用于設置你的密鑰, 這個密鑰(幾乎)每次都會用到。

不用說,你應該小心保管好你的密鑰,如果有人得到了你的密鑰,那么數據就能很容易的被解密。 如果你的服務器不在你的控制之下,想保證你的密鑰絕對安全是不可能的, 所以在在你使用密鑰對敏感數據(譬如信用卡號碼)進行加密之前,請再三斟酌。

你的加密密鑰的長度 必須 滿足正在使用的加密算法允許的長度。例如,AES-128 算法最長支持 128 位(16 字節(jié))。下面有一個表列出了不同算法支持的密鑰長度。

你所使用的密鑰應該越隨機越好,它不能是一個普通的文本字符串,經過哈希函數處理過也不行。 為了生成一個合適的密鑰,你應該使用加密類提供的create_key() 方法:

// $key will be assigned a 16-byte (128-bit) random key
$key = $this->encryption->create_key(16);

密鑰可以保存在 application/config/config.php 配置文件中,或者你也可以設計你自己的存儲機制, 然后加密解密的時候動態(tài)的去獲取它。

如果要保存在配置文件 application/config/config.php 中,可以打開該文件,然后設置:

$config['encryption_key'] = 'YOUR KEY';

你會發(fā)現 create_key() 方法返回的是二進制數據,沒辦法復制粘貼,所以你可能還需要使用 bin2hex() 、 hex2bin() 或 Base64 編碼來更好的處理密鑰數據。例如:

// Get a hex-encoded representation of the key:
$key = bin2hex($this->encryption->create_key(16));

// Put the same value in your config with hex2bin(),
// so that it is still passed as binary to the library:
$config['encryption_key'] = hex2bin(<your hex-encoded key>);

支持的加密算法和模式

可移植的算法(Portable ciphers)

因為 MCrypt 和 OpenSSL (我們也稱之為“驅動”)支持的加密算法不同,而且實現方式也不太一樣, CodeIgniter 將它們設計成一種可移植的方式來使用,換句話說,你可以交換使用它們兩個, 至少對它們兩個驅動都支持的算法來說是這樣。

而且 CodeIgniter 的實現也和其他編程語言和類庫的標準實現一致。

下面是可移植算法的清單,其中 "CodeIgniter 名稱" 一欄就是你在使用加密類的時候使用的名稱:

算法名稱 CodeIgniter 名稱 密鑰長度 (位 / 字節(jié)) 支持的模式
AES-128 / Rijndael-128 aes-128 128 / 16 CBC, CTR, CFB, CFB8, OFB, ECB
AES-192 aes-192 192 / 24 CBC, CTR, CFB, CFB8, OFB, ECB
AES-256 aes-256 256 / 32 CBC, CTR, CFB, CFB8, OFB, ECB
DES des 56 / 7 CBC, CFB, CFB8, OFB, ECB
TripleDES tripledes 56 / 7, 112 / 14, 168 / 21 CBC, CFB, CFB8, OFB
Blowfish blowfish 128-448 / 16-56 CBC, CFB, OFB, ECB
CAST5 / CAST-128 cast5 88-128 / 11-16 CBC, CFB, OFB, ECB
RC4 / ARCFour rc4 40-2048 / 5-256 Stream

重要

由于 MCrypt 的內部實現,如果你提供了一個長度不合適的密鑰,它會使用另一種不同的算法來加密, 這將和你配置的算法不一致,所以要特別注意這一點!

注解

上表中還有一點要澄清,Blowfish、CAST5 和 RC4 算法支持可變長度的密鑰,也就是說, 只要密鑰的長度在指定范圍內都是可以的。

注解

盡管 CAST5 支持的密鑰的長度可以小于 128 位(16 字節(jié)),其實實際上,根據 RFC 2144 我們知道,它會用 0 進行補齊到最大長度。

注解

Blowfish 算法支持最短 32 位(4 字節(jié))的密鑰,但是經過我們的測試發(fā)現,只有密鑰長度大于等于 128 位(16 字節(jié)) 時,才可以很好的同時支持 MCrypt 和 OpenSSL ,再說,設置這么短的密鑰也不是好的做法。

特定驅動的算法(Driver-specific ciphers)

正如前面所說,MCrypt 和 OpenSSL 支持不同的加密算法,所以你也可以選擇下面這些只針對某一特定驅動的算法。 但是為了移植性考慮,而且這些算法也沒有經過徹底測試,我們并不建議你使用這些算法。

算法名稱 驅動 密鑰長度 (位 / 字節(jié)) 支持的模式
AES-128 OpenSSL 128 / 16 CBC, CTR, CFB, CFB8, OFB, ECB, XTS
AES-192 OpenSSL 192 / 24 CBC, CTR, CFB, CFB8, OFB, ECB, XTS
AES-256 OpenSSL 256 / 32 CBC, CTR, CFB, CFB8, OFB, ECB, XTS
Rijndael-128 MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Rijndael-192 MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Rijndael-256 MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
GOST MCrypt 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Twofish MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
CAST-128 MCrypt 40-128 / 5-16 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
CAST-256 MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Loki97 MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
SaferPlus MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
Serpent MCrypt 128 / 16, 192 / 24, 256 / 32 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
XTEA MCrypt 128 / 16 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
RC2 MCrypt 8-1024 / 1-128 CBC, CTR, CFB, CFB8, OFB, OFB8, ECB
RC2 OpenSSL 8-1024 / 1-128 CBC, CFB, OFB, ECB
Camellia-128 OpenSSL 128 / 16 CBC, CFB, CFB8, OFB, ECB
Camellia-192 OpenSSL 192 / 24 CBC, CFB, CFB8, OFB, ECB
Camellia-256 OpenSSL 256 / 32 CBC, CFB, CFB8, OFB, ECB
Seed OpenSSL 128 / 16 CBC, CFB, OFB, ECB

注解

如果你要使用這些算法,你只需將它的名稱以小寫形式傳遞給加密類即可。

注解

你可能已經注意到,所有的 AES 算法(以及 Rijndael-128 算法)也在上面的可移植算法列表中出現了, 這是因為這些算法支持不同的模式。還有很重要的一點是,在使用 128 位的密鑰時,AES-128 和 Rijndael-128 算法其實是一樣的。

注解

CAST-128 / CAST-5 算法也在兩個表格都出現了,這是因為當密鑰長度小于等于 80 位時, OpenSSL 的實現貌似有問題。

注解

列表中可以看到 RC2 算法同時被 MCrypt 和 OpenSSL 支持,但是兩個驅動對它的實現方式是不一樣的, 而且也是不能移植的。我們只找到了一條關于這個的不確定的消息可能是 MCrypt 的實現有問題。

加密模式

加密算法的不同模式有著不同的特性,它們有著不同的目的,有的可能比另一些更強壯,有的可能速度更快, 有的可能提供了額外的功能。 我們并不打算深入研究這個,這應該是密碼學專家做的事。下表將向我們普通的用戶列出一些簡略的參考信息。 如果你是個初學者,直接使用 CBC 模式就可以了,一般情況下它已經足夠強壯和安全,并且已經被廣泛接受。

模式名稱 CodeIgniter 名稱 支持的驅動 備注
CBC cbc MCrypt, OpenSSL 安全的默認選擇
CTR ctr MCrypt, OpenSSL 理論上比 CBC 更好,但并沒有廣泛使用
CFB cfb MCrypt, OpenSSL N/A
CFB8 cfb8 MCrypt, OpenSSL 和 CFB 一樣,但是使用 8 位模式(不推薦)
OFB ofb MCrypt, OpenSSL N/A
OFB8 ofb8 MCrypt 和 OFB 一樣,但是使用 8 位模式(不推薦)
ECB ecb MCrypt, OpenSSL 忽略 IV (不推薦)
XTS xts OpenSSL 通常用來加密可隨機訪問的數據,如 RAM 或 硬盤
Stream stream MCrypt, OpenSSL 這其實并不是一種模式,只是表明使用了流加密,通常在 算法+模式 的初始化過程中會用到。

消息長度

有一點對你來說可能很重要,加密的字符串通常要比原始的文本字符串要長(取決于算法)。

這個會取決于加密所使用的算法,添加到密文上的 IV ,以及添加的 HMAC 認證信息。 另外,為了保證傳輸的安全性,加密消息還會被 Base64 編碼。

當你選擇數據保存機制時請記住這一點,譬如 Cookie 只能存儲 4k 的信息。

配置類庫

考慮到可用性,性能,以及一些歷史原因,加密類使用了和老的 加密類 一樣的驅動、 加密算法、模式 和 密鑰。

上面的 "默認行為" 一節(jié)已經提到,系統將自動檢測驅動(OpenSSL 優(yōu)先級要高點),使用 CBC 模式的 AES-128 算法,以及$config['encryption_key'] 參數。

如果你想改變這點,你需要使用 initialize() 方法,它的參數為一個關聯數組,每一項都是可選:

選項 可能的值
driver 'mcrypt', 'openssl'
cipher 算法名稱(參見 支持的加密算法和模式
mode 加密模式(參見 加密模式
key 加密密鑰

例如,如果你想將加密算法和模式改為 AES-126 CTR ,可以這樣:

$this->encryption->initialize(
    array(
        'cipher' => 'aes-256',
        'mode' => 'ctr',
        'key' => '<a 32-character random string>'
    )
);

另外,我們也可以設置一個密鑰,如前文所說,針對所使用的算法選擇一個合適的密鑰非常重要。

我們還可以修改驅動,如果你兩種驅動都支持,但是出于某種原因你想使用 MCrypt 來替代 OpenSSL

// Switch to the MCrypt driver
$this->encryption->initialize(array('driver' => 'mcrypt'));

// Switch back to the OpenSSL driver
$this->encryption->initialize(array('driver' => 'openssl'));

對數據進行加密與解密

使用已配置好的參數來對數據進行加密和解密是非常簡單的,你只要將字符串傳給 encrypt() 和/或 decrypt() 方法即可:

$plain_text = 'This is a plain-text message!';
$ciphertext = $this->encryption->encrypt($plain_text);

// Outputs: This is a plain-text message!
echo $this->encryption->decrypt($ciphertext);

這樣就行了!加密類會為你完成所有必須的操作并確保安全,你根本不用關系細節(jié)。

重要

兩個方法在遇到錯誤時都會返回 FALSE ,如果是 encrypt() 返回 FALSE , 那么只可能是配置參數錯了。在生產代碼中一定要對 decrypt() 方法進行檢查。

實現原理

如果你非要知道整個過程的實現步驟,下面是內部的實現:

  • $this->encryption->encrypt($plain_text)
    1. 通過 HKDF 和 SHA-512 摘要算法,從你配置的 encryption_key 參數中獲取兩個密鑰:加密密鑰 和 HMAC 密鑰。
    2. 生成一個隨機的初始向量(IV)。
    3. 使用上面的加密密鑰和 IV ,通過 AES-128 算法的 CBC 模式(或其他你配置的算法和模式)對數據進行加密。
    4. 將 IV 附加到密文后。
    5. 對結果進行 Base64 編碼,這樣就可以安全的保存和傳輸它,而不用擔心字符集問題。
    6. 使用 HMAC 密鑰生成一個 SHA-512 HMAC 認證消息,附加到 Base64 字符串后,以保證數據的完整性。
  • $this->encryption->decrypt($ciphertext)
    1. 通過 HKDF 和 SHA-512 摘要算法,從你配置的 encryption_key 參數中獲取兩個密鑰:加密密鑰 和 HMAC 密鑰。 由于 encryption_key 不變,所以生成的結果和上面 encrypt() 方法生成的結果是一樣的,否則你沒辦法解密。
    2. 檢查字符串的長度是否足夠長,并從字符串中分離出 HMAC ,然后驗證是否一致(這可以防止時序攻擊(timing attack)), 如果驗證失敗,返回 FALSE 。
    3. 進行 Base64 解碼。
    4. 從密文中分離出 IV ,并使用 IV 和 加密密鑰對數據進行解密。

使用自定義參數

假設你需要和另一個系統交互,這個系統不受你的控制,而且它使用了其他的方法來加密數據, 加密的方式和我們上面介紹的流程不一樣。

在這種情況下,加密類允許你修改它的加密和解密的流程,這樣你就可以簡單的調整成自己的解決方案。

注解

通過這種方式,你可以不用在配置文件中配置 encryption_key 就能使用加密類。

你所需要做的就是傳一個包含一些參數的關聯數組到 encrypt() 或 decrypt() 方法,下面是個例子:

// Assume that we have $ciphertext, $key and $hmac_key
// from on outside source

$message = $this->encryption->decrypt(
    $ciphertext,
    array(
        'cipher' => 'blowfish',
        'mode' => 'cbc',
        'key' => $key,
        'hmac_digest' => 'sha256',
        'hmac_key' => $hmac_key
    )
);

在上面的例子中,我們對一段使用 CBC 模式的 Blowfish 算法加密的消息進行解密,并使用 SHA-256 HMAC 認證方式。

重要

注意在這個例子中 'key' 和 'hmac_key' 參數都要指定,當使用自定義參數時,加密密鑰和 HMAC 密鑰 不再是默認的那樣從配置參數中自動獲取的了。

下面是所有可用的選項。

但是,除非你真的需要這樣做,并且你知道你在做什么,否則我們建議你不要修改加密的流程,因為這會影響安全性, 所以請謹慎對待。

選項 默認值 必須的 / 可選的 描述
cipher N/A Yes 加密算法(參見 支持的加密算法和模式
mode N/A Yes 加密模式(參見 加密模式
key N/A Yes 加密密鑰
hmac TRUE No 是否使用 HMAC 布爾值,如果為 FALSE ,hmac_digest 和 hmac_key 將被忽略
hmac_digest sha512 No HMAC 消息摘要算法(參見 支持的 HMAC 認證算法
hmac_key N/A Yes,除非 hmac 設為 FALSE HMAC 密鑰
raw_data FALSE No 加密文本是否保持原樣 布爾值,如果為 TRUE ,將不執(zhí)行 Base64 編碼和解碼操作 HMAC 也不會是十六進制字符串

重要

encrypt() and decrypt() will return FALSE if a mandatory parameter is not provided or if a provided value is incorrect. This includeshmac_key, unless hmac is set to FALSE.

支持的 HMAC 認證算法

對于 HMAC 消息認證,加密類支持使用 SHA-2 家族的算法:

算法 原始長度(字節(jié)) 十六進制編碼長度(字節(jié))
sha512 64 128
sha384 48 96
sha256 32 64
sha224 28 56

之所以沒有包含一些其他的流行算法,譬如 MD5 或 SHA1 ,是因為這些算法目前已被證明不夠安全, 我們并不鼓勵使用它們。如果你非要使用這些算法,簡單的使用 PHP 的原生函數 hash_hmac() 也可以。

當未來出現廣泛使用的更好的算法時,我們自然會將其添加進去。

類參考

classCI_Encryption

initialize($params)

參數:

  • $params (array) -- Configuration parameters

返回: CI_Encryption instance (method chaining)

返回類型: CI_Encryption

初始化加密類的配置,使用不同的驅動,算法,模式 或 密鑰。

例如:

$this->encryption->initialize(
    array('mode' => 'ctr')
);

請參考 配置類庫 一節(jié)了解詳細信息。

encrypt($data[, $params = NULL])

參數:

  • $data (string) -- Data to encrypt
  • $params (array) -- Optional parameters

返回: Encrypted data or FALSE on failure

返回類型: string

對輸入數據進行加密,并返回密文。

例如:

$ciphertext = $this->encryption->encrypt('My secret message');

請參考 使用自定義參數 一節(jié)了解更多參數信息。

decrypt($data[, $params = NULL])

參數:

  • $data (string) -- Data to decrypt
  • $params (array) -- Optional parameters

返回: Decrypted data or FALSE on failure

返回類型: string

對輸入數據進行解密,并返回解密后的文本。

例如:

echo $this->encryption->decrypt($ciphertext);

請參考 使用自定義參數 一節(jié)了解更多參數信息。

create_key($length)

參數:

  • $length (int) -- Output length

返回: A pseudo-random cryptographic key with the specified length, or FALSE on failure

返回類型: string

從操作系統獲取隨機數據(例如 /dev/urandom),并生成加密密鑰。

hkdf($key[, $digest = 'sha512'[, $salt = NULL[, $length = NULL[, $info = '']]]])

參數:

  • $key (string) -- Input key material
  • $digest (string) -- A SHA-2 family digest algorithm
  • $salt (string) -- Optional salt
  • $length (int) -- Optional output length
  • $info (string) -- Optional context/application-specific info

返回: A pseudo-random key or FALSE on failure

返回類型: string

從一個密鑰生成另一個密鑰(較弱的密鑰)。

這是內部使用的一個方法,用于從配置的 encryption_key 參數生成一個加密密鑰和 HMAC 密鑰。

將這個方法公開,是為了可能會在其他地方使用到。關于這個算法的描述可以看 RFC 5869 。

和 RFC 5869 描述不同的是,這個方法不支持 SHA1 。

例如:

$hmac_key = $this->encryption->hkdf(
    $key,
    'sha512',
    NULL,
    NULL,
    'authentication'
);

// $hmac_key is a pseudo-random key with a length of 64 bytes
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號