密碼學之DES/AES演算法

飛鴻影發表於2019-02-16

本文示例程式碼詳見: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會被設定成

相關文章