65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (54投票s)

2001 年 9 月 27 日

Ms-PL

3分钟阅读

viewsIcon

984894

downloadIcon

12853

本文介绍 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 的倍数。此函数可以在以下模式下运行:ECBCBCCFB。 在 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" 被加密,然后在所有操作模式(ECBCBCCFB)中解密回来。 请注意,对于链接操作模式,必须在解密之前重置链块。

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类的源代码和一些测试代码。

© . All rights reserved.