如何从种子生成加密强大的随机字节序列?

时间:2013-10-29 23:24:12

标签: c# random cryptography prng

如何从种子值生成一系列加密强随机字节(以便可以再次从同一种子重新生成序列)?是否有任何可以在C#中实现的优秀CSPRNG算法(最好有良好的文档)?

Niether RNGCryptoServiceProviderRandom类将满足我的要求,因为Random不是加密强,RNGCryptoServiceProvider不允许您设置种子值。

1 个答案:

答案 0 :(得分:1)

Rfc2898DeriveBytes非常适合这项工作,通常它被用作密码散列函数,但是你可以根据需要从中请求任意数量的字节,并且它总是会为给定的字节返回相同的字节数种子(密码,盐和迭代次数的组合)

以下是来自MSDN的示例,其中显示了两个Rfc2898DeriveBytes实例为两者返回相同的序列(通过使用第一个序列来加密具有对称加密的数据块并使用第二个序列对其进行解密)

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

public class rfc2898test
{
    // Generate a key k1 with password pwd1 and salt salt1. 
    // Generate a key k2 with password pwd1 and salt salt1. 
    // Encrypt data1 with key k1 using symmetric encryption, creating edata1. 
    // Decrypt edata1 with key k2 using symmetric decryption, creating data2. 
    // data2 should equal data1. 

    private const string usageText = "Usage: RFC2898 <password>\nYou must specify the password for encryption.\n";
    public static void Main(string[] passwordargs)
    {
        //If no file name is specified, write usage text. 
        if (passwordargs.Length == 0)
        {
            Console.WriteLine(usageText);
        }
        else
        {
            string pwd1 = passwordargs[0];
            // Create a byte array to hold the random value.  
            byte[] salt1 = new byte[8];
            using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
            {
                // Fill the array with a random value.
                rngCsp.GetBytes(salt1);
            }

            //data1 can be a string or contents of a file. 
            string data1 = "Some test data";
            //The default iteration count is 1000 so the two methods use the same iteration count.
            int myIterations = 1000;
            try
            {
                Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1, salt1, myIterations);
                Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1);
                // Encrypt the data.
                TripleDES encAlg = TripleDES.Create();
                encAlg.Key = k1.GetBytes(16);
                MemoryStream encryptionStream = new MemoryStream();
                CryptoStream encrypt = new CryptoStream(encryptionStream, encAlg.CreateEncryptor(), CryptoStreamMode.Write);
                byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(data1);

                encrypt.Write(utfD1, 0, utfD1.Length);
                encrypt.FlushFinalBlock();
                encrypt.Close();
                byte[] edata1 = encryptionStream.ToArray();
                k1.Reset();

                // Try to decrypt, thus showing it can be round-tripped.
                TripleDES decAlg = TripleDES.Create();
                decAlg.Key = k2.GetBytes(16);
                decAlg.IV = encAlg.IV;
                MemoryStream decryptionStreamBacking = new MemoryStream();
                CryptoStream decrypt = new CryptoStream(decryptionStreamBacking, decAlg.CreateDecryptor(), CryptoStreamMode.Write);
                decrypt.Write(edata1, 0, edata1.Length);
                decrypt.Flush();
                decrypt.Close();
                k2.Reset();
                string data2 = new UTF8Encoding(false).GetString(decryptionStreamBacking.ToArray());

                if (!data1.Equals(data2))
                {
                    Console.WriteLine("Error: The two values are not equal.");
                }
                else
                {
                    Console.WriteLine("The two values are equal.");
                    Console.WriteLine("k1 iterations: {0}", k1.IterationCount);
                    Console.WriteLine("k2 iterations: {0}", k2.IterationCount);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: ", e);
            }

        }
    }
}