1

我正在努力学习网络安全,这是我做的第一件事。我正在使用这个 MSDN 文档(https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rfc2898derivebytes?redirectedfrom=MSDN&view=netcore-3.1),它部分有效。我认为它加密得很好,因为在解密时,一些原始数据在那里,但有些丢失了。正在加密的数据是一个已格式化为 JSON 字符串的类(我认为这无关紧要,因为它仍然是一个正在加密的字符串)。 原始数据

但是一旦它被加密和解密,结果是这样的: 加密然后解密的数据

我已经运行了这段代码并比较了 5 次以上的结果,它总是:开始错误,用户名部分正确,密码始终正确,loginkey 部分正确。所以错误反复出现,并且总是在同一个地方。

您应该知道的信息,数据被加密并保存到 .txt 文件中。该程序将再次运行并尝试解密它。Salt和密码保存在另一个文件中,并在解密中读取和使用。

stackoverflow 上有一个类似的问题,但答案只是说要使用 Rijndael(所以不是真正的答案),这段代码是让我学习并想要一个不长 4 行的答案。

好奇的代码(但它与MSDN文档基本相同):

加密:

    static void EncryptFile()
    {
        string pwd1 = SteamID;//steamID is referring to account ID on Valve Steam           
        using (RNGCryptoServiceProvider rngCsp = new
        RNGCryptoServiceProvider())
        {
            rngCsp.GetBytes(salt1); //salt1 is a programme variable and will get saved to a file
        }
        SecureData File = new SecureData(_UserName,_PassWord,_LoginKey);
        string JsonFile = JsonConvert.SerializeObject(File); //puts the class into Json format
        int myIterations = 1000; //not needed
        try
        {
            Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1,
            myIterations);
            Aes encAlg = Aes.Create(); // This might be the issue as AES will be different when you decrypt 
            encAlg.Key = k1.GetBytes(16);
            MemoryStream encryptionStream = new MemoryStream();
            CryptoStream encrypt = new CryptoStream(encryptionStream,
            encAlg.CreateEncryptor(), CryptoStreamMode.Write);
            byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(
            JsonFile); //encrypt Data 

            encrypt.Write(utfD1, 0, utfD1.Length);
            encrypt.FlushFinalBlock();
            encrypt.Close();
            byte[] edata1 = encryptionStream.ToArray();
            k1.Reset();

            System.IO.File.WriteAllBytes(SecureFile, edata1); //writes encrypted data to file
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: ", e);
        }
    }

解密:

    static void DecryptFile()
    {
        string pwd1 = SteamID;
        byte[] edata1;
        try
        {
            edata1 = System.IO.File.ReadAllBytes(SecureFile); //reads the file with encrypted data on it
            Aes encAlg = Aes.Create(); //I think this is the problem as the keyvalue changes when you create a new programme
            Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1); //inputs from last time carry over
            Aes decAlg = Aes.Create();
            decAlg.Key = k2.GetBytes(16);
            decAlg.IV = encAlg.IV;
            MemoryStream decryptionStreamBacking = new MemoryStream();
            CryptoStream decrypt = new CryptoStream(
            decryptionStreamBacking, decAlg.CreateDecryptor(), CryptoStreamMode.Write);
            decrypt.Write(edata1, 0, edata1.Length);
            decrypt.Flush();
            decrypt.Close();
            k2.Reset();
            string data2 = new UTF8Encoding(false).GetString(
            decryptionStreamBacking.ToArray());//decrypted data  
            SecureData items = JsonConvert.DeserializeObject<SecureData>(data2); //reformat it out of JSon(Crashes as format isn't accepted)
            _UserName = items.S_UserName;
            _PassWord = items.S_Password;
            _LoginKey = items.S_LoginKey;
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: ", e);
            NewLogin();
        }
    }

类结构:

    class SecureData
    {
        public string S_UserName { get; set; } //These are variables that are apart of Valve steam Login process 
        public string S_Password { get; set; }
        public string S_LoginKey { get; set; }

        public SecureData(string z, string x, string y)
        {
            S_UserName = z;
            S_Password = x;
            S_LoginKey = y;
        }
    }
4

1 回答 1

2

该问题是由用于加密和解密的不同 IV 引起的。为了成功解密,必须使用来自加密的 IV。

为什么要应用不同的 IV?创建AES实例时,会隐式生成随机 IV。因此,两个不同的AES实例意味着两个不同的 IV。在发布的代码中,不同的AES实例用于加密和解密。虽然encAlg解密时使用的引用与加密时的引用同名,但引用的实例是不同的实例(即解密时新创建的实例)。这在 Microsoft 示例中有所不同。在那里,加密的 IV 用于解密:decAlg.IV = encAlg.IV,其中encAlgAES执行加密的实例。

解决方案是将加密的 IV 存储在文件中,以便在解密时使用它。IV 不是秘密的,通常放在密文之前:

必要的改变EncryptFile

...
byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(JsonFile); 

encryptionStream.Write(encAlg.IV, 0, encAlg.IV.Length);           // Write the IV
encryptionStream.Flush();
            
encrypt.Write(utfD1, 0, utfD1.Length);
...
            

必要的改变DecryptFile

...
edata1 = System.IO.File.ReadAllBytes(SecureFile); 
            
byte[] iv = new byte[16];                                         // Separate IV and ciphertext
byte[] ciphertext = new byte[edata1.Length - iv.Length];
Array.Copy(edata1, 0, iv, 0, iv.Length);
Array.Copy(edata1, iv.Length, ciphertext, 0, ciphertext.Length);
...     
Aes encAlg = Aes.Create();                                        // Remove this line
...
decAlg.IV = iv;                                                   // Use the separated IV
...
decrypt.Write(ciphertext, 0, ciphertext.Length);                  // Use the separated ciphertext

几点说明:

  • 对于每次加密,都应该生成一个新的随机盐,并将其与类似于 IV 的密文连接起来。在解密期间,然后可以类似于 IV 来确定盐。另外考虑RFC8018,第 4.1 节
  • 迭代计数减慢了密钥的派生速度,这会使重复尝试的攻击更加困难。因此该值应尽可能大。另外考虑RFC8018,第 4.2 节
  • 身份验证数据(即密码)没有加密,而是经过哈希处理,这里
于 2020-07-31T07:54:30.883 回答