AES 填充并将密文写入磁盘文件

Posted

技术标签:

【中文标题】AES 填充并将密文写入磁盘文件【英文标题】:AES padding and writing the ciphertext to a disk file 【发布时间】:2015-02-07 17:30:33 【问题描述】:

我有一个字符串,我使用 Crypto++ 在 C++ 中使用以下方法对其进行加密:

std::ifstream t(filename); //File to be encrypt
std::stringstream buffer;
buffer << t.rdbuf();

ofstream combined_file2(filename2); //Encrypted file
combined_file2 << encrypt(buffer.str());

string encrypt(string data)

  // Key and IV setup
  std::string key = "0123456789abcdef";
  std::string iv = "aaaaaaaaaaaaaaaa";

  //Alternative
  //byte key[CryptoPP::AES::DEFAULT_KEYLENGTH], iv[CryptoPP::AES::BLOCKSIZE];
  //memset(key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH);
  //memset(iv, 0x00, CryptoPP::AES::BLOCKSIZE);

  std::string plaintext = data;
  std::string ciphertext;

  // Create Cipher Text
  CryptoPP::AES::Encryption aesEncryption((byte *)key.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH);
  CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, (byte *)iv.c_str());

  //Alternative
  //CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
  //CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);

  CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(ciphertext));
  stfEncryptor.Put(reinterpret_cast<const unsigned char*>(plaintext.c_str()), plaintext.length() + 1);
  stfEncryptor.MessageEnd();

  return ciphertext;

当我尝试在我的 C# 应用程序中解密文件时,我收到一条消息,即数据的长度无效。我认为字节数组的长度不是 16 的倍数,所以我得到了错误。我尝试使用填充,但它无法正常工作。

这是我解密文件的方法:

string plaintext = Decrypt(File.ReadAllBytes(path));

private static string Decrypt(byte[] cipherText)

   if (cipherText == null || cipherText.Length <= 0)
       throw new ArgumentNullException("cipherText");
   byte[] Key = GetBytes(ConfigurationManager.AppSettings["aes_key"]);
   byte[] IV = GetBytes(ConfigurationManager.AppSettings["aes_iv"]);

   // Declare the string used to hold the decrypted text.
   string plaintext = null;

   // Create an RijndaelManaged object with the specified key and IV.
   using (RijndaelManaged rijAlg = new RijndaelManaged())
   
     rijAlg.Key = Key;
     //rijAlg.IV = IV;
     //for testing                
     rijAlg.IV = new byte[]  97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97 ;

    // Create a decrytor to perform the stream transform.
    ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

    // Create the streams used for decryption.
    using (MemoryStream msDecrypt = new MemoryStream(cipherText))
    
      using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
      
        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
        
          // Read the decrypted bytes from the decrypting stream
          // and place them in a string.
          plaintext = srDecrypt.ReadToEnd();
        
      
    
  
  return plaintext;

我该如何解决这个问题?是否有任何功能可以进行填充,或者填充是错误的方式?

【问题讨论】:

如何将密文从C++端传输到C#端? 在 C++ 中,我保存了一个文件 combined_file2 &lt;&lt; encrypt(buffer.str());,我在我的 C# 程序 string plaintext = Decrypt(File.ReadAllBytes(path)); 中读取了该文件 通常你会得到这种异常,如果 - 在链中的某个地方 - 密文被转换为字符串而不是二进制。 我正在使用这个示例进行加密:programmingknowledgeblog.blogspot.de/2013/04/… 我也在没有 +1 的情况下对其进行了测试,但问题仍然存在。密文为byte[238533]。 你能告诉我们你是如何保护字符串的吗?加密后的字符串是什么大小?注意,空终止符不应使用来确定大小,字符串应被视为字节。 【参考方案1】:

根据评论:

在 C++ 中,我保存了一个文件 combine_file2

我不认为嵌入的 NULL 会导致 encrypt 函数出现问题,因为它返回了 string,其中包括显式长度。

但是,嵌入的 NULL 以及文件在 C++ 中写入磁盘(或在 C# 中从磁盘读取)的方式将是一个问题,因为这将在第一个嵌入的 NULL 处停止:combined_file2 &lt;&lt; encrypt(buffer.str())

也许以下内容会有所帮助:

StringSource ss(ciphertext, true /*pumpAll*/);
FileSink fs("my-encrypted-file.bin", true /*binary*/);
ss.TransferTo(fs);

如果您使用的是 C++ 流,则在流对象上使用 write 方法:

ofstream combined_file2;
...
combined_file2.write(ciphertext.data(), ciphertext.size());

【讨论】:

以上是关于AES 填充并将密文写入磁盘文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost-asio 时实时将缓冲区写入磁盘

序列化对象并将其写入磁盘

将数据从缓冲区写入磁盘上的波形文件[关闭]

AES中输入和密文长度之间的关系

加密算法之AES

RSA+AES请求组合加密