1

我正在研究 VS2015 中的 CryptSharp SCrypt 实现。我需要加密/解密要作为电子邮件附件发送的文本文件。最初我使用的是 AES,但考虑到 HMAC-SHA1 已经过时,我选择使用 SCrypt 进行密码散列。但是,SCrypt 并没有公开数据加密本身的公共方法,那么将 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;
    }
4

1 回答 1

2

但是,SCrypt 并没有公开数据加密本身的公共方法,因此将 SCrypt 散列密码传递给 AES 是否有意义

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

如何可靠地随机化 IV?

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

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

我还建议您对文件进行 MAC 处理,因为现在您的应用程序没有对加密进行身份验证

于 2016-11-16T19:17:55.680 回答