C# Rijndael加密函数-密钥/IV/Salt设置

C# Rijndael加密函数-密钥/IV/Salt设置,c#,security,encryption,C#,Security,Encryption,我正在开发一个简单的实用程序来加密文件,作为一种学习体验。 一切似乎都很正常,但我想知道我是否已经安全地设置了密钥/IV/Salt数据 我注意到,当涉及到密码学时,大多数人似乎设想一个工作环境,该环境中装载有恶意软件,由一个向导远程操控,随时准备通过运行中的应用程序/页面文件的内存来获取这些安全文件 让我们假设你在一台干净的机器上,加密一些文件并关闭计算机。 我想知道的是,是否有人可以使用我提出的代码获取您的硬盘并检索文件的内容 P>攻击向量是我最关心的,就是确保页面文件/文件缓存是不可访问的

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

我注意到,当涉及到密码学时,大多数人似乎设想一个工作环境,该环境中装载有恶意软件,由一个向导远程操控,随时准备通过运行中的应用程序/页面文件的内存来获取这些安全文件

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

<> P>攻击向量是我最关心的,就是确保页面文件/文件缓存是不可访问的。 我还想确保使用的Key/IV系统不会使基于彩虹表/散列的攻击变得可行

输入密码:

使用passwordchar值设置为true的文本框输入密码

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

private static string salt = "02341235XadfaDADFexA8932F7Dz3J3X";
我使用一个硬编码的32字符字符串来设置密码。 (上面的字符串只是一个示例。)

要做到这一点,需要有人用十六进制编辑器反编译/查看.exe文件本身(我知道这很容易做到,但还是需要额外的一步)

我已经考虑过让这个盐可以编辑,但我不确定如何安全地保存它。我认为加密你的salt有点可笑,因为那样你就会有同样的问题等等,所以把它作为一个硬编码字符串放在exe里面对我来说是最有意义的

其工作方式是,如果您决定将密码设置为“密码”,它实际上会保存为“密码FADADFEXA8932F7DZ3J3X”

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

钥匙和IV:

钥匙和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;
                }
            }
        }
    }
}
感谢您的时间,请告诉我是否有更好的方法来设置密钥/IV/Salt。 我认为,只要IV和密钥包含相似字符时不存在数学问题,它就很可能足够安全。如果是这样,我是否也应该使用硬编码IV?这似乎很奇怪

请注意,我没有保存密码散列或类似的内容。密码不会保存在任何位置。它只是用来生成密钥和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
            }
        }
    }
}
据我所知,

  • IV/盐不需要是私人的。它可以以纯文本形式存储在硬盘上。事实上,salt必须是纯文本,否则无法生成相同的输出
  • 使用您的关键信息生成IV不是一个好主意,因为它可能会泄露您的关键信息
  • 你无法阻止像彩虹这样的攻击。但是有了盐,彩虹攻击就变得昂贵了,因为塔布只能使用这种盐值
  • 有一个密钥派生函数的标准可能对您有用()

  • 盐有两个用途:

  • 防止彩虹表攻击(如果应用正确,则会发生)
  • 防止相同的密码生成相同的密码哈希
  • 要做到这一点,salt需要8到16字节(不是字符)的随机数据,与密码散列一起存储。使用静态哈希会破坏哈希的两个目的

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

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

    现在可以从PBKDF2函数获取IV(使用
    GetBytes
    )。但是有一个问题,这可能会使PBKDF2函数的初始迭代次数增加一倍,这会消耗CPU时间,并降低与攻击者相比的优势。最好只生成一个随机IV,并将其作为密文的前缀


    因此,最后你应该存储
    salt | IV |密文
    ,然后使用
    salt | pepper
    作为salt并计算你的密钥,然后使用计算出的密钥和IV进行加密/解密。

    不要使用固定的salt,salt的意义在于,如果密码被重用,你无法判断它是否被重用。salt不需要保密,只需将其附加到正在加密的文件的前面即可