【问题标题】:In VB.NET, writing AES key and encrypted data to same MemoryStream not working在 VB.NET 中,将 AES 密钥和加密数据写入相同的 MemoryStream 不起作用
【发布时间】:2011-06-24 15:59:22
【问题描述】:

我正在尝试使用对称加密来加密一些数据,并将解密所需的密钥与数据一起存储在 MemoryStream 中。 (虽然我知道仅此一项在安全方面确实很愚蠢,但我将使用 RSA 来加密对称密钥。不过,现在,我正试图让这部分工作。)

我正在使用 FileHelpers 库为我的分隔符解析数据(分号,因为我不相信数据中会有分号)。不幸的是,在我的解密函数中,当它解析时,它只返回一条记录。而且,如果我显示在此函数末尾创建的整个加密数据字符串,它似乎并没有使用多条记录。

我想知道当我创建新的加密流时,它是否默认为内存流的开头,所以当我写入加密数据时,它会覆盖我刚刚写入内存流的数据。你觉得对吗?

感谢您的帮助!

Private Function Encrypt(ByVal data As String) As String
    Dim utf8 As New UTF8Encoding
    ' Convert string data to byte array
    Dim inBytes() As Byte = utf8.GetBytes(data)
    ' Create memory stream for storing the data we've manipulated
    Dim ms As New MemoryStream()

    Dim aes As New RijndaelManaged()
    aes.KeySize = 256

    Dim cs As New CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)

    ' Write key to beginning of memory stream
    ms.Write(aes.Key, 0, aes.Key.Length)
    ' Add semicolon delimiter to memory stream
    ms.Write(utf8.GetBytes(";"), 0, utf8.GetBytes(";").Length)
    ' Write IV to memory stream
    ms.Write(aes.IV, 0, aes.IV.Length)
    ' Write semicolon delimiter to memory stream
    ms.Write(utf8.GetBytes(";"), 0, utf8.GetBytes(";").Length)
    ' Ensure that the data we've just written is in the memory stream, before
    ' we add the encrypted data
    ms.Flush()

    ' Write the encrypted data
    cs.Write(inBytes, 0, inBytes.Length) ' encrypt
    cs.FlushFinalBlock()

    ' Return encrypted data as string
    Return Convert.ToBase64String(ms.GetBuffer(), 0, ms.Length)
End Function

【问题讨论】:

    标签: vb.net security cryptography encryption-symmetric


    【解决方案1】:

    以下是如何实现此目标的快速示例。我将EncryptDecrypt 都包括在内,因为在两边都遵循相同的模式显然很重要。我会把相关的错误检查等留给你:)

    Imports System.Security.Cryptography
    Imports System.IO
    Imports System.Text
    
    Module Module1
    
      Sub Main()
        Dim encrypted As String = Encrypt("Here is the data")
        Dim decrypted As String = Decrypt(encrypted)
        Console.WriteLine(decrypted)
        Console.ReadKey()
      End Sub
    
      Private Function Encrypt(clearText As String) As String
        Dim aes As New RijndaelManaged
        aes.KeySize = 256
    
        Using ms As New MemoryStream
          ms.WriteByte(aes.Key.Length)
          ms.Write(aes.Key, 0, aes.Key.Length)
          ms.WriteByte(aes.IV.Length)
          ms.Write(aes.IV, 0, aes.IV.Length)
    
          Using cs As New CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write)
            Dim bytes() As Byte = Encoding.UTF8.GetBytes(clearText)
            cs.Write(bytes, 0, bytes.Length)
          End Using
    
          Return Convert.ToBase64String(ms.ToArray())
        End Using
      End Function
    
      Private Function Decrypt(cipherText As String) As String
        Dim ms As New MemoryStream(Convert.FromBase64String(cipherText))
    
        Dim keyLength As Byte = ms.ReadByte()
        Dim key(keyLength - 1) As Byte
        ms.Read(key, 0, keyLength)
    
        Dim ivLength As Byte = ms.ReadByte()
        Dim iv(ivLength - 1) As Byte
        ms.Read(iv, 0, ivLength)
    
        Dim dataOffset As Integer = ms.Position
    
        Dim aes As New RijndaelManaged()
        aes.Key = key
        aes.IV = iv
    
        Using cs As New CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read)
          Using sr As New StreamReader(cs, Encoding.UTF8)
            Return sr.ReadToEnd()
          End Using
        End Using
      End Function
    End Module
    

    【讨论】:

      【解决方案2】:

      你为什么要写内存流?直接写入 Crypto 流,然后从内存流中读取加密数据。这是 AES 加密的 C# 版本。

      aesEncryptor 是你的aes.CreateEncryptor()

              private byte[] encryptWithAes(ICryptoTransform aesEncryptor, byte[] data)
              {
                  MemoryStream memStream = null; //stream to write encrypted data to
                  CryptoStream cryptoStream = null; //crypto stream to encrypt data
      
                  try
                  {
                      memStream = new MemoryStream(); 
      
                      //initiate crypto stream telling it to write the encrypted data to the memory stream
                      cryptoStream = new CryptoStream(memStream, aesEncryptor, CryptoStreamMode.Write);
      
                      //write the data to the memory stream
                      cryptoStream.Write(data, 0, data.Length);
                  }
                  catch (Exception ee)
                  {
                      //rethrow
                      throw new Exception("Error while encrypting with AES: ", ee);
                  }
                  finally
                  {
                      //close 'em
                      if (cryptoStream != null)
                          cryptoStream.Close();
                      if (memStream != null)
                          memStream.Close();
                  }
      
                  //return the encrypted data
                  return memStream.ToArray();
              }
      

      可以这样调用:

      public byte[] doEncrypt(byte[] dataToEncrypt)
      {
                  RijndaelManaged aesAlg = new RijndaelManaged();
                  aesAlg.KeySize = AES_KEY_SIZE;
                  aesAlg.GenerateKey();
                  aesAlg.GenerateIV();
                  aesAlg.Mode = CipherMode.CBC;
                  aesAlg.Padding = PaddingMode.PKCS7;
      
                  byte[] aesKey = aesAlg.Key;
                  byte[] aesIV = aesAlg.IV;
      
                  byte[] encryptedData = encryptWithAes(aesAlg.CreateEncryptor(), dataToEncrypt);
      
                  //store your aesKey, aesIV, and encryptedData together however you want
      }
      

      您不应该使用任何字符删除二进制数据,因为当您再次读取该数据时,您必须对整个缓冲区进行编码,包括密钥和 IV,一旦编码,您的分隔符可能会随机出现在任何地方。而是将密钥的长度存储为一个字节,然后再存储密钥。当你去读回数据时,你可以把第一个字节(密钥长度)读成一个整数,然后知道要读多少才能得到密钥。这对你有意义吗?

      【讨论】:

      • 我正在将密钥和 IV 写入内存流,因为我想保留它以便可以解密数据。我的目标是每次随机生成对称密钥,然后用户使用他们的 RSA 密钥对其进行加密。因此,我需要将对称密钥与加密数据一起存储。
      • @Sam,该内存流仅应由加密流用于将加密数据写入(并供您从中读取加密数据)。如果要存储 IV 和密钥,请使用不同的缓冲区,然后将其附加到从内存流中读取的加密数据中
      • @Sam,添加了获取 IV、密钥和加密数据的示例
      • @Petey 哦!我似乎在互联网上搜索了糟糕的一天,因为我找不到组合两个内存流的示例。我会这样做吗?创建一个内存流来保存密钥和 IV。使用加密流创建一个内存流来保存加密数据。再创建一个memorystream,然后把第一个memorystream的内容写进去,再写第二个?
      • @Sam,为什么要将它们保存在内存流中?尝试用';'分隔不是一个好主意在内存流中(当您重新编码时,尝试读取分隔符,加密的数据/密钥可能在任何地方都有';')。最好将第一个字节存储为密钥的长度,然后存储密钥,然后存储下一个字节作为 IV 的长度,然后是 IV,然后剩下的就是加密数据。
      猜你喜欢
      • 2018-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      相关资源
      最近更新 更多