Blowfish 加密/解密方法的 C++ 实现






4.89/5 (54投票s)
本文介绍 Blowfish 加密/解密方法的 C++ 实现
引言
Blowfish 是一种加密算法,可以用来替代 DES 或 IDEA 算法。它是一种对称(即加密和解密使用相同的密钥)分组密码(以 8 字节块加密数据),它使用可变长度的密钥,从 32 位(4 字节)到 448 位(56 字节)。Blowfish 由 Bruce Schneier 于 1993 年设计,作为现有加密算法的替代品。它的设计考虑了 32 位指令处理器,因此比 DES 快得多。自问世以来,它已经过大量分析。 Blowfish 未获得专利,免许可,并且可免费用于所有用途。 该算法包括两个部分:密钥扩展部分和数据加密部分。密钥扩展将至少 4 个字节且最多 56 个字节的可变密钥转换为总计 4168 个字节的几个子密钥数组。 Blowfish 有 16 轮。每一轮都包含一个与密钥相关的置换,以及一个与密钥和数据相关的替换。所有操作都是 32 位字上的异或和加法运算。唯一的附加操作是每轮四个索引数组数据查找。 Blowfish 使用大量的子密钥。在任何数据加密或解密之前,必须预先计算这些密钥。
有关 Blowfish 算法的更详细文章可以在这里找到: http://www.schneier.com/blowfish.html
本文中提供的 C++ 实现已经过 Eric Young 在 http://www.counterpane.com/vectors.txt 提供的测试向量测试。结果完全相同,但有一个例外,我认为可能是参考 Internet 文件中的打字错误。
实现
CBlowfish
类的公共用户界面如下所示
class CBlowFish { public: //Constructor - Initialize the P and S boxes for a given Key CBlowFish(unsigned char* ucKey, size_t n,
const SBlock& roChain = SBlock(0UL,0UL)); //Resetting the chaining block void ResetChain(); // Encrypt/Decrypt Buffer in Place void Encrypt(unsigned char* buf, size_t n, int iMode=ECB); void Decrypt(unsigned char* buf, size_t n, int iMode=ECB); // Encrypt/Decrypt from Input Buffer to Output Buffer void Encrypt(const unsigned char* in, unsigned char* out, size_t n, int iMode=ECB); void Decrypt(const unsigned char* in, unsigned char* out, size_t n, int iMode=ECB); };
在构造函数中,使用指定大小的用户提供的密钥材料来生成子密钥数组。 并且,使用指定的值初始化链块。
函数 ResetChain()
用于在开始新的加密或解密操作之前重置链接块。
Encrypt()
函数的第一个变体用于对指定大小的数据块进行就地加密,并应用指定的操作模式。 块大小应为 8 的倍数。此函数可以在以下模式下运行:ECB、CBC 或 CFB。 在 ECB 模式下,不使用链接。 如果使用相同的密钥对同一块进行两次加密,则生成的密文块是相同的。 在 CBC 模式下,首先将明文块与先前的密文块进行异或运算,然后对结果值进行加密,从而获得密文块。 在 CFB 模式下,通过加密先前的密文块并将结果值与明文异或来获得密文块。 操作模式在 iMode
参数中指定,默认为 ECB。 对于 Encrypt()
函数的第二个变体,加密结果在输出缓冲区中传递。
Decrypt()
函数是上面介绍的 Encrypt()
函数的反向操作。
使用示例
CBlowfish
类的使用非常简单。 在第一个代码段示例中,将 8 字节大小的密钥应用于 8 字节的块。 初始链块是一个空块。 块 "aaaabbbb"
被加密,然后解密回来。
try { char szHex[17]; //Initialization CBlowFish oBlowFish((unsigned char*)"abcdefgh", 8); char szDataIn[] = "aaaabbbb"; char szDataOut[17] = "\0\0\0\0\0\0\0\0"; //Encryption oBlowFish.Encrypt((unsigned char*)szDataIn, (unsigned char*)szDataOut, 8); CharStr2HexStr((unsigned char*)szDataIn, szHex, 8); cout << szHex << endl; CharStr2HexStr((unsigned char*)szDataOut, szHex, 8); cout << szHex << endl; memset(szDataIn, 0, 8); //Decryption oBlowFish.Decrypt((unsigned char*)szDataOut, (unsigned char*)szDataIn, 8); CharStr2HexStr((unsigned char*)szDataIn, szHex, 8); cout << szHex << endl; } catch(exception& roException) { cout << roException.what() << endl; }
在下一个代码段示例中,将 16 字节大小的密钥应用于更大的 48 字节大小的数据块(数据块的大小应为块大小的倍数,块大小始终为 8 字节)。 初始链块是一个空块。 块 "ababababccccccccababababccccccccababababcccccccc"
被加密,然后在所有操作模式(ECB、CBC 和 CFB)中解密回来。 请注意,对于链接操作模式,必须在解密之前重置链块。
try { CBlowFish oBlowFish((unsigned char*)"1234567890123456", 16); char szDataIn1[49] = "ababababccccccccababababccccccccababababcccccccc"; char szDataIn[49]; char szDataOut[49]; memset(szDataIn, 0, 49); memset(szDataOut, 0, 49); //Test ECB strcpy(szDataIn, szDataIn1); memset(szDataOut, 0, 49); oBlowFish.Encrypt((unsigned char*)szDataIn, (unsigned char*)szDataOut, 48, CBlowFish::ECB); memset(szDataIn, 0, 49); oBlowFish.Decrypt((unsigned char*)szDataOut, (unsigned char*)szDataIn, 48, CBlowFish::ECB); //Test CBC oBlowFish.ResetChain(); strcpy(szDataIn, szDataIn1); memset(szDataOut, 0, 49); oBlowFish.Encrypt((unsigned char*)szDataIn, (unsigned char*)szDataOut, 48, CBlowFish::CBC); memset(szDataIn, 0, 49); oBlowFish.ResetChain(); oBlowFish.Decrypt((unsigned char*)szDataOut, (unsigned char*)szDataIn, 48, CBlowFish::CBC); //Test CFB oBlowFish.ResetChain(); strcpy(szDataIn, szDataIn1); memset(szDataOut, 0, 49); oBlowFish.Encrypt((unsigned char*)szDataIn, (unsigned char*)szDataOut, 48, CBlowFish::CFB); memset(szDataIn, 0, 49); oBlowFish.ResetChain(); oBlowFish.Decrypt((unsigned char*)szDataOut, (unsigned char*)szDataIn, 48, CBlowFish::CFB); cout << endl; } catch(exception& roException) { cout << "Exception: " << roException.what() << endl; }
我对有关此实现的任何意见和新想法感兴趣。 附加到本文的项目Blowfish.zip包括所提供的CBlowfish
类的源代码和一些测试代码。