【问题标题】:How can I generate a cryptographically strong sequence of random bytes from a seed?如何从种子中生成加密强的随机字节序列?
【发布时间】:2013-10-29 23:24:12
【问题描述】:

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

RNGCryptoServiceProviderRandom 类都可以满足我的要求,因为 Random 的密码强度不高,RNGCryptoServiceProvider 不允许您设置种子值。

【问题讨论】:

  • 假设它是用于测试 - 为什么不使用只返回众所周知的数字序列的特殊实现? (否则可重复序列有多“加密强度”?)
  • @AlexeiLevenkov 因为我需要根据种子生成不同的序列。 Random 类会很好,如果它在密码学上很强大。
  • 加密安全的随机生成器意味着无法预测数字。此外,数字(或字节)cannot be random - 生成器是。

标签: c# random cryptography prng


【解决方案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);
            }

        }
    }
}

【讨论】:

  • iterations=1 非常慢,而 1000 次迭代则非常慢。如果您从 PBKDF2 中消耗超过 20 个字节并进行大量迭代,那么您可能做错了什么。
猜你喜欢
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-17
  • 1970-01-01
  • 2021-05-15
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多