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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (71投票s)

2001年9月19日

Ms-PL

3分钟阅读

viewsIcon

902212

downloadIcon

22708

一篇文章介绍了 Rijndael (AES) 加密/解密方法的 C++ 实现。

引言

Rijndael(发音为 rain-dahl)是由美国国家标准与技术研究院 (NIST) 选为高级加密标准 (AES) 候选者的块密码算法。 它从五个最终候选者中脱颖而出,而这五个候选者又是从最初的 15 个以上提交方案中选出的。 在接下来的几年里,Rijndael 将开始取代数据加密标准 (DES) - 以及后来的三重 DES - 在许多密码学应用中。 该算法由两位比利时密码学家 Vincent Rijmen 和 Joan Daemen 设计,他们的姓氏反映在密码的名称中。 Rijndael 的起源在于 Square,这是两位密码学家之间早期的合作成果。

该密码具有可变的块长度和密钥长度。 作者目前指定了如何使用长度为 128、192 或 256 位的密钥来加密长度为 128、192 或 256 位的块(密钥长度和块长度的所有九种组合都是可能的)。 块长度和密钥长度都可以很容易地扩展为 32 位的倍数。 可以在 此处 找到该方法的文档和完整规范(NIST 要求)。

Rijndael 可以在各种处理器和硬件上非常有效地实现。 此处介绍的 C++ 实现基于 Internet 上找到的 Cryptix 工具包使用的 Java 实现 此处 (Java 代码作者是 Raif S. Naffah 和 Paulo S.L.M. Barreto)。 针对该方法作者发布的 KAT 测试对该实现进行了测试,结果完全相同。

实现

CRijndael 类的公共用户界面如下所示

class CRijndael
{
public:
  //Operation Modes
  enum { ECB=0, CBC=1, CFB=2 };

  //CONSTRUCTOR
  CRijndael();

  //DESTRUCTOR
  virtual ~CRijndael();

  //Expand a user-supplied key material into a session key.
  void MakeKey(char const* key, char const* chain, 
    int keylength=DEFAULT_BLOCK_SIZE, 
    int blockSize=DEFAULT_BLOCK_SIZE);

  //Encrypt exactly one block of plaintext.
  void EncryptBlock(char const* in, char* result);

  //Decrypt exactly one block of ciphertext.
  void DecryptBlock(char const* in, char* result);

  //Encrypt a larger block of data
  void Encrypt(char const* in, char* result, size_t n, int iMode=ECB);

  //Decrypt a larger block of data
  void Decrypt(char const* in, char* result, size_t n, int iMode=ECB);

  //Get Key Length
  int GetKeyLength();

  //Block Size
  int GetBlockSize();

  //Number of Rounds
  int GetRounds();

  //Resets chain block to initial chain block.
  void ResetChain();

  //Null chain block
  static char const* sm_chain0;
};

MakeKey() 函数用于将用户提供的密钥材料扩展为会话密钥并初始化链块。 keylengthblockSize 可以是 16、24 或 32 字节大小的任何组合,其中 DEFAULT_BLOCK_SIZE 为 16。 在调用任何其他函数之前,必须在构造之后调用此函数。

EncryptBlock() 函数用于使用指定的密钥加密指定大小的块。

DecryptBlock() 函数是 EncryptBlock() 函数的逆函数,用于使用指定的密钥解密指定大小的块。

Encrypt() 函数用于加密更大的数据块。 块大小必须是该方法块大小的倍数。 此函数可以在以下模式下运行:ECBCBCCFBECB 模式不使用链。 如果使用相同的密钥对同一个块加密两次,则生成的密文块相同。 在 CBC 模式下,通过首先将明文块与前一个密文块进行异或运算,然后对结果值进行加密来获得密文块。 在 CFB 模式下,通过加密前一个密文块并将结果值与明文进行异或运算来获得密文块。 操作模式在 iMode 参数中指定,ECB 为默认值。

Decrypt() 函数是 Encrypt() 函数的逆函数。

其他函数的功能显而易见。

使用示例

CRijndael 类的使用非常简单。 在第一个代码片段示例中,将 16 字节的块大小和密钥大小应用于 16 字节的块。 初始链块是空块。 块 "aaaaaaaabbbbbbbb" 被加密,然后被解密回原样。

try
{
  char szHex[33];

  //Initialization
  CRijndael oRijndael;
  oRijndael.MakeKey("abcdefghabcdefgh", CRijndael::sm_chain0, 16, 16);

  char szDataIn[] = "aaaaaaaabbbbbbbb";
  char szDataOut[17] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

  //Encryption
  oRijndael.EncryptBlock(szDataIn, szDataOut);

  CharStr2HexStr((unsigned char*)szDataIn, szHex, 16);
  cout << szHex << endl;
  CharStr2HexStr((unsigned char*)szDataOut, szHex, 16);
  cout << szHex << endl;
  memset(szDataIn, 0, 16);

  //Decryption
  oRijndael.DecryptBlock(szDataOut, szDataIn);

  CharStr2HexStr((unsigned char*)szDataIn, szHex, 16);
  cout << szHex << endl;
}
catch(exception& roException)
{
  cout << roException.what() << endl;
}

在下一个代码片段示例中,将 16 字节的块大小和密钥大小应用于大小为 48 字节的较大数据块(数据块的大小应该是块大小的倍数)。 初始链块是空块。 块 "ababababccccccccababababccccccccababababcccccccc" 在所有操作模式(ECBCBCCFB)下被加密然后解密回原样。

try
{
  CRijndael oRijndael;
  oRijndael.MakeKey("1234567890123456", CRijndael::sm_chain0, 16, 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);
  oRijndael.Encrypt(szDataIn, szDataOut, 48, CRijndael::ECB);
  memset(szDataIn, 0, 49);
  oRijndael.Decrypt(szDataOut, szDataIn, 48, CRijndael::ECB);

  //Test CBC
  oRijndael.ResetChain();
  strcpy(szDataIn, szDataIn1);
  memset(szDataOut, 0, 49);
  oRijndael.Encrypt(szDataIn, szDataOut, 48, CRijndael::CBC);
  memset(szDataIn, 0, 49);
  oRijndael.ResetChain();
  oRijndael.Decrypt(szDataOut, szDataIn, 48, CRijndael::CBC);

  //Test CFB
  oRijndael.ResetChain();
  strcpy(szDataIn, szDataIn1);
  memset(szDataOut, 0, 49);
  oRijndael.Encrypt(szDataIn, szDataOut, 48, CRijndael::CFB);
  memset(szDataIn, 0, 49);
  oRijndael.ResetChain();
  oRijndael.Decrypt(szDataOut, szDataIn, 48, CRijndael::CFB);
}
catch(exception& roException)
{
  cout << "Exception: " << roException.what() << endl;
}

我对这个实现中的任何意见和新想法都感兴趣。 附加到这篇文章的项目 AES.zip 包含了所呈现的 CRijndael 类的源代码和测试代码。

历史

  • 2002年11月8日 - 更新下载。
Rijndael 加密/解密方法的 C++ 实现 - CodeProject - 代码之家
© . All rights reserved.