本文示例程式碼詳見:https://github.com/52fhy/cryp…
DES
DES全稱為Data Encryption Standard,即資料加密標準,是一種使用金鑰加密的塊演算法,1977年被美國聯邦政府的國家標準局確定為聯邦資料處理標準(FIPS),並授權在非密級政府通訊中使用,隨後該演算法在國際上廣泛流傳開來。
DES使用簡介
使用DES需要設定加密內容、加密key、加密混淆向量iv、分組密碼模式、填充模式。
加密內容:
給定的加密的資料。如果資料長度不是 n*分組大小,則在其後使用 `0` 補齊。
加密Key:
加密金鑰。 如果金鑰長度不是該演算法所能夠支援的有效長度,需要填充。如果金鑰長度過長,需要擷取。
加密iv:
用於CBC, CFB, OFB模式,在ECB模式裡不是必須的。
分組密碼模式:
常見的分組密碼模式有:CBC, OFB,CFB 和 ECB。
填充模式:
Pkcs5、Pkcs7。
填充演算法(Pkcs5、Pkcs7)
PKCS5Padding與PKCS7Padding基本上是可以通用的。在PKCS5Padding中,明確定義Block的大小是8位,而在PKCS7Padding定義中,對於塊的大小是不確定的,可以在1-255之間(塊長度超出255的尚待研究),填充值的演算法都是一樣的:
pad = k - (l mod k) //k=塊大小,l=資料長度,如果k=8, l=9,則需要填充額外的7個byte的7
可以得出:Pkcs5是Pkcs7的特例(Block的大小始終是8位)。當Block的大小始終是8位的時候,Pkcs5和Pkcs7是一樣的。(參考)
填充演算法實現:
- PHP
function pkcs5_pad($text) {
$pad = 8 - (strlen($text) % 8);
//$pad = 8 - (strlen($text) & 7); //也可以使用這種方法
return $text . str_repeat(chr($pad), $pad);
}
function pkcs7_pad ($text, $blocksize) {
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
反填充(去掉填充的字元)只需要根據解密後內容最後一個字元,就知道填充了什麼、填充了幾個,然後擷取掉即可:
function _unpad($text){
$pad = ord(substr($text, -1));//取最後一個字元的ASCII 碼值
if ($pad < 1 || $pad > strlen($text)) {
$pad = 0;
}
return substr($text, 0, (strlen($text) - $pad));
}
- Python
from Crypto.Cipher import AES
def pkcs7_pad(str):
x = AES.block_size - (len(str) % AES.block_size)
if x != 0:
str = str + chr(x)*x
return str
def _unpad(msg):
paddingLen = ord(msg[len(msg)-1])
return msg[0:-paddingLen]
加密解密步驟
加密步驟(以PHP的擴充套件mcrypt為例):
1、獲得加密演算法的分組大小(mcrypt_get_block_size);
2、被加密的明文使用Pkcs5或Pkcs7填充;
3、加密金鑰key擷取或填充至8位;
4、加密向量iv設定;
5、開啟指定演算法和模式對應的模組,返回加密描述符td(mcrypt_module_open);
6、使用td、key、iv初始化加密所需的緩衝區 (mcrypt_generic_init);
7、加密資料(mcrypt_generic);
8、清理的加密描述符td的緩衝區(mcrypt_generic_deinit);
9、釋放加密描述符td(mcrypt_module_close);
10、返回base64_encode的加密結果,可選。
解密步驟(以PHP的擴充套件mcrypt為例):
1、base64_decode解碼,如果加密使用了base64_encode;
2、加密金鑰key擷取或填充至8位;
3、加密向量iv設定;
4、開啟指定演算法和模式對應的模組,返回加密描述符td(mcrypt_module_open);
5、使用td、key、iv初始化加密所需的緩衝區 (mcrypt_generic_init);
6、解密資料(mdecrypt_generic);
7、清理的加密描述符td的緩衝區(mcrypt_generic_deinit);
8、釋放加密描述符td(mcrypt_module_close);
9、使用Pkcs5去掉填充的內容,返回解密後的結果。
使用DES需要注意下面幾點:
1) 確保都使用DES
+ ECB
;
2) 確保明文填充都使用的是Pkcs5
或者Pkcs7
,此時兩者效果一致;
3) 加密key在DES長度必須是8位元組(bytes);如果不夠長必須填充,過長必須擷取;
4) 加密向量iv與加密key有同樣的約定;
5) 注意加密結果建議都使用base64編碼。
只有以上都保持一樣,各個語言裡最終加密的密文才能保持一致,否則會出現:
1) 每次加密的密文不一樣,但是能解密;(iv隨機生成導致的)
2) 不同語言加密出來的密文不一致。
各種語言實現示例
PHP
示例:
- Crypt_DES.php
<?php
include(`Crypt_DES.php`);
$des = new Crypt_DES();//預設是CBC模式
$plaintext = `123456`;
$des->setKey(`pwd`);
//$des->setIV(" ");//預設填0,注意是雙引號
$encode = base64_encode($des->encrypt($plaintext));
echo $encode. PHP_EOL;
echo $des->decrypt(base64_decode($encode));
注意:Crypt_DES類裡預設是MCRYPT_MODE_CBC
模式,且預設會把加密向量擷取或填充至8位:
str_pad(substr($key, 0, 8), 8, chr(0))
也就是如果加密向量大於8位,只會擷取前8位;少於則補0。
另外加密向量iv
會被設定成