C# 中的 CryptSharp SCrypt 实现

CryptSharp SCrypt implementation in C#

我正在研究 VS2015 中的 CryptSharp SCrypt 实现。我需要 encrypt/decrypt 个要作为电子邮件附件发送的文本文件。最初我使用的是 AES,但考虑到 HMAC-SHA1 已经过时,我选择使用 SCrypt 进行密码哈希处理。但是,SCrypt 本身并未公开 public 数据加密方法,那么将 SCrypt 散列密码传递给 AES,然后使用后者进行数据加密是否有意义?或者也许有更好的方法?

在这种情况下,我会想象这样的事情,但我需要找到一种方法来可靠地随机化 IV...

    private static Aes SetAes(string userName, string password)
    {            
        var passBytes = Encoding.UTF8.GetBytes(password);
        var saltBytes = Encoding.UTF8.GetBytes(userName);
        var cost = 131072; // around 5 secs with block at 16(on Xeon 1241 v3)
        var blockSize = 16; // 8 is default but might not suffice against modern GPUs(?)
        var parallel = 1;
        var maxThreads = (int?)null;
        byte[] derivedKey = new byte[32]; // 256 bits

        SCrypt.ComputeKey(passBytes, saltBytes, cost, blockSize, parallel, maxThreads, derivedKey);                                              

        Aes aes = new AesManaged();
        aes.Padding = PaddingMode.PKCS7;            
        aes.Key = derivedKey;
        byte[] IV = new byte[16];
        Array.Copy(derivedKey, IV, 16); // how to reliably randomize the IV?
        aes.IV = IV;
        return aes;
    }

然后进行文件加密:

    internal static void EncryptText(string text, string userName, string password, string file)
    {
        // omitting argument checks for readability
        using (Aes aes = SetAes(userName, password))
        {
            using (FileStream fileStream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
            {
                using (CryptoStream cryptoStream = new CryptoStream(fileStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                {                        
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(cryptoStream, text); // I'm using a class to wrap the text for serialization, not shown here for readability                        
                }
            }
        }
    }

尽管它似乎有效,但我不确定它是否有意义,所以非常感谢您的任何见解。

编辑:

按照 vcsjones 的建议,SetAes 函数看起来像这样,如果我理解正确的话:

    private static Aes SetAes(string userName, string password, byte[] IV = null)
    {            
        var passBytes = Encoding.UTF8.GetBytes(password);
        var saltBytes = Encoding.UTF8.GetBytes(userName);
        var cost = 131072; 
        var blockSize = 16; 
        var parallel = 1;
        var maxThreads = (int?)null;
        byte[] derivedKey = new byte[32]; 

        SCrypt.ComputeKey(passBytes, saltBytes, cost, blockSize, parallel, maxThreads, derivedKey);                                              

        Aes aes = new AesManaged();
        aes.Padding = PaddingMode.PKCS7;            
        aes.Key = derivedKey;
        if (IV == null) // when encrypting, generate IV
        {
            RandomNumberGenerator rn = RandomNumberGenerator.Create();
            rn.GetBytes(aes.IV);
        }
        else aes.IV = IV; // when decrypting, read IV from file and pass it to aes through IV parameter for decryption                                    
        return aes;
    }

However, SCrypt does not expose public methods for the data encryption itself, so would it make sense to pass the SCrypt hashed password to AES

SCrypt 是一个密钥派生函数,所以是的,这是可以接受的事情。

how to reliably randomize the IV?

不要在 IV 中使用 KDF 的输出。 AES-CBC 的 IV 应该是随机的,因此使用 RandomNumberGenerator.Create() 为 IV 创建一个 CSPRNG。使用 KDF 输出作为 IV 的一部分实际上会泄露密钥,因为 IV 是以明文形式存储的。

AES-CBC 中的 IV 应随机,不应重复使用。不要从密码中导出它。您确实需要将 IV 存储在某个地方。由于看起来您正在尝试加密文件,因此您可能只想将 IV 放在文件的开头。 IV 不是秘密 - 如果有人可以阅读它就可以了。然后,当需要解密文件时,从文件中读取 IV,然后通过 IV 解密所有内容。

我还建议您 MAC 该文件,因为现在您的应用程序没有 authenticate the encryption