Rijndael 加密函数 - Key/IV/Salt 设置

Rijndael Cryptography Function - Key/IV/Salt Setup

我正在开发一个简单的实用程序来加密文件,作为一种学习体验。 一切似乎都运行良好,但我想知道我是否已安全地设置 Key/IV/Salt 数据。

我注意到,在谈到密码学时,大多数人似乎都设想了一个工作环境,该环境加载了由远程向导操纵的恶意软件,准备潜入正在运行的 application/page 文件的内存中以获取这些安全文件。

假设您在一台干净的机器上,您加密了一些文件并关闭了计算机。 我想知道是否有人可以使用我建议的代码拿走你的硬盘并检索文件的内容。

我最关心的攻击向量是确保页面 files/file 缓存不可访问。 我还想确保所使用的 Key/IV 系统不会使基于彩虹 table/hash 的攻击变得可行。

正在输入密码:

使用文本框输入密码,并将 passwordchar 值设置为 true。

我不太关心内存中的字符串,只要它在加密后被正确删除即可。我读到在这一点上使用 SecureString 有点毫无意义,因为如果你的计算机上已经有恶意软件,你可以很容易地在那里安装一个键盘记录器,这会使其他一切都变得无用。

private static string salt = "02341235XadfaDADFexA8932F7Dz3J3X";

我使用硬编码的 32 个字符串对密码进行加盐。 (以上字符串只是一个例子。)

要做到这一点,需要有人使用十六进制编辑器 decompile/view .exe 文件本身(我知道这很容易做到,但仍然需要额外的步骤)。

我考虑过让这个盐可编辑,但我不确定如何安全地存储它。我认为加密你的盐有点荒谬,因为那样你会遇到同样的问题等,所以把它作为一个硬编码字符串留在 exe 本身似乎对我来说最有意义。

如果您决定设置密码 "thepassword",它的工作方式实际上是保存为 "thepasswordfaDADFexA8932F7Dz3J3X"。

这里的关键是无论您输入什么,您始终拥有一个 32 个字符的密码。

密钥和 IV:

Key和IV也加盐如下。 这就是我想要得到的一些意见,因为老实说我不完全确定它在做什么:

UnicodeEncoding UE = new UnicodeEncoding();
byte[] keysalt = UE.GetBytes("Xjafe231x42X423XadXCadfkhjeAdS");        //Another string of random characters hard coded in the exe
byte[] IVSodium = UE.GetBytes("83Xkda7l78Dkx85KdJazppoqq6SaxDs");        //Another string of random characters hard coded in the exe
byte[] key = new Rfc2898DeriveBytes(password, keysalt).GetBytes(32);   //Derive the key using the password and salt
byte[] IV = new Rfc2898DeriveBytes(password, IVSodium).GetBytes(16);    //Derive the IV using the password and salt

我主要担心的是 IV 是基于密钥的。同样,我不确定这是否会导致任何问题,我希望你们能告诉我是否有问题,它们是什么。

此外,这是另一种情况,其中硬编码 salt 是一种不好的做法吗?这是否应该存储在加密文件中,如果是这样,它真的会更安全吗?我是否也应该将其设为可编辑?

使用 using 关键字设置加密流:

using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
    using (RijndaelManaged RMCrypto = new RijndaelManaged())
    {
        using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
        {
            using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
            {
                byte[] buffer = new byte[4096]; //4096 is kind of arbitrary - better idea?
                int data;
                long bytesRead = 0;
                while((data = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    bytesRead += data;
                    /////////////////////////////////////////
                    // Handle Aborts and Update Progress Bar
                    /////////////////////////////////////////
                    if (!caller.isClosing)
                        caller.Invoke((MethodInvoker)delegate {
                            caller.fileProgressBar.Value = ((int)(((double)bytesRead / totalBytes) * 100)); 
                        });
                    else
                        return false; //Encryption Aborted
                    /////////////////////////////////////////
                    cs.Write(buffer, 0, data);
                    fsIn.Close();
                    cs.Close();
                    fsCrypt.Close();
                    return true;
                }
            }
        }
    }
}

感谢您的宝贵时间,如果有更好的设置 Key/IV/Salt 的方法,请告诉我。 我认为只要包含相似字符的 IV 和 Key 不存在数学问题,它就很可能足够安全。如果是这样,我是否也应该使用硬编码 IV?这看起来很奇怪。

请注意,我没有保存密码的哈希值或类似的东西。密码不会保存在任何地方。它只是用来生成Key和IV。

谢谢你的时间。

编辑:以下是为未来展望的任何人推荐的更改。

请注意,这不是使用胡椒粉 - 只是一种随机盐,尽管添加

很容易
byte[] salt = new byte[32];                                        //Create a 32 byte salt
rand.NextBytes(salt);                                              //Fill it with random values (use RandomNumberGenerator rand = new RNGCryptoServiceProvider(); to be safe
byte[] IV = new byte[16];                                          //Create a 16 byte IV
rand.NextBytes(IV);                                                //Fill it with random numbers
byte[] key = new Rfc2898DeriveBytes(password, salt).GetBytes(32);  //Derive our Key by mixing our password with the salt

using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
    using (RijndaelManaged RMCrypto = new RijndaelManaged())
    {
        using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
        {
            using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
            {
                fsCrypt.Write(salt, 0, salt.Length); //Write our salt to the file
                fsCrypt.Write(IV, 0, IV.Length);     //Write our IV to the file
                fsIn.CopyTo(cs);                     //Encrypt and Write
            }
        }
    }
}

据我所知,

  1. IV/Salt 不需要是私有的。它可以以纯文本形式存储在硬盘上。事实上,salt 必须是纯文本的,否则你不能用它生成相同的输出。
  2. 使用您的密钥信息生成 IV 不是一个好主意,因为它可能会泄露您的密钥信息。
  3. 没有办法阻止像彩虹一样的攻击。但是有了盐,彩虹攻击变得昂贵,因为 talbes 只适用于这个盐值。
  4. 有一个密钥推导函数的标准,可能对你有用(http://en.wikipedia.org/wiki/PBKDF2)。

盐有两个用途:

  1. 防止彩虹 table 攻击(如果应用正确,它 );
  2. 防止相同的密码生成相同的密码哈希值。

为此,salt 需要 8 到 16 字节(不是字符)的随机数据,与密码哈希一起存储。像您一样使用静态散列会破坏散列的两个目的。

如果您需要字符串,请使用 base 64 对盐和密码哈希进行编码。如果需要,可以在调用密码哈希函数之前将静态数据(有时称为 "pepper")添加到盐中。如果攻击者无法轻易读取程序数据,这可能会增加一些安全性。

切勿自己直接混合盐和密码; Rfc2898DeriveBytes(它是 PBKDF2 的一个实现)已经混合了两者。您也不应存储密码,也不应向其附加任何数据。 PBKDF2 可以处理任何大小的输入,因此它不会添加任何功能。

现在可以从 PBKDF2 函数中获取 IV(使用 GetBytes)。然而,存在一个问题,这很可能会使 PBKDF2 函数的初始迭代次数加倍,这会花费 CPU 时间并降低对攻击者的优势。最好只生成一个随机 IV 并将其作为密文的前缀。

所以最后你应该存储 salt | IV | ciphertext,然后使用 salt | pepper 作为盐并计算你的密钥,然后 encrypt/decrypt 使用计算出的密钥和 IV。