C#中的CryptSharp SCrypt实现

时间:2016-11-16 19:11:22

标签: c# scrypt cryptsharp

我正在开发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;
    }

1 个答案:

答案 0 :(得分: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,因为你的应用程序现在不是authenticate the encryption