密码随机的唯一字符串

时间:2014-06-03 13:40:50

标签: c# random cryptography unique

this回答中,发布了以下代码,用于创建唯一的随机字母数字字符串。有人可以向我澄清他们在这段代码中是如何确保它们的独特性以及它们在多大程度上是独一无二的?如果我在不同的场合重新运行这种方法,我还会得到独特的字符串吗?

或者我只是误解了答案,这些根本没有生成唯一的密钥,只是随机的?

我已在对该答案的评论中问过这个问题,但用户似乎处于非活动状态。

    public static string GetUniqueKey()
    {
        int maxSize = 8;
        char[] chars = new char[62];
        string a;
        a = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
        chars = a.ToCharArray();
        int size = maxSize;
        byte[] data = new byte[1];
        RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
        crypto.GetNonZeroBytes(data);
        size = maxSize;
        data = new byte[size];
        crypto.GetNonZeroBytes(data);
        StringBuilder result = new StringBuilder(size);
        foreach (byte b in data)
        { result.Append(chars[b % (chars.Length - 1)]); }
        return result.ToString();
    }   

4 个答案:

答案 0 :(得分:6)

代码中没有任何内容可以保证结果是唯一的。要获得唯一值,您必须保留所有先前的值,以便检查重复项,或使用更长的代码,以便实际上不可能重复(例如GUID)。该代码包含少于48位的信息,这比GUID的128位少得多。

字符串只是随机的,虽然使用了加密强度随机生成器,但这会被随机数据生成代码的方式所破坏。代码中存在一些问题:

  • 创建一个char数组,它被丢弃并替换为另一个。
  • 根本没有明显的原因创建一个单字节的随机数据数组,因为它没有用于任何事情。
  • 使用GetNonZeroBytes方法代替GetBytes方法,这会增加字符分布的偏差,因为代码无法处理零值的缺失。
  • 模(%)运算符用于将随机数减少到使用的字符数,但随机数不能均匀分为字符数,这也会增加偏差人物的分布。
  • 当数量减少时,使用
  • chars.Length - 1代替chars.Length,这意味着字符串中只能出现61个预定义的62个字符。

虽然这些问题很小,但在处理密码强度随机性时它们很重要。

将产生没有这些问题的字符串的代码版本,并提供具有足够信息的代码,以便被认为是实际上唯一的:

public static string GetUniqueKey() {
  int size = 16;
  byte[] data = new byte[size];
  RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();
  crypto.GetBytes(data);
  return BitConverter.ToString(data).Replace("-", String.Empty);
}

答案 1 :(得分:3)

独特性和随机性是相互排斥的概念。如果随机数生成器是真正随机的,那么它可以返回相同的值。如果值确实是唯一的,虽然它们可能不是确定性的,但它们当然不是真正随机的,因为生成的每个值都会从允许值池中删除一个值。这意味着每次运行都会影响后续运行的结果,并且在某个时刻池已经耗尽(当然除了允许值无限大的池的可能性,但避免这种池中冲突的唯一方法是使用确定性的方法选择价值观。)

您显示的代码会生成非常随机的值,但并非100%保证是唯一的。经过足够的运行后,成为碰撞。

答案 2 :(得分:1)

我需要生成7个字母数字字符串。经过一小段搜索,我编写了以下代码。效果结果已上传到上方

我使用hashtable Class来保证唯一性,还使用RNGCryptoServiceProvider Class来获得高质量的随机字符

results of generating 100.000 - 1.000.000 - 10.000.000 sample

生成唯一的字符串; thanks to nipul parikh

    public static Tuple<List<string>, List<string>> GenerateUniqueList(int count)
        {
            uniqueHashTable = new Hashtable();
            nonUniqueList = new List<string>();
            uniqueList = new List<string>();

            for (int i = 0; i < count; i++)
            {
                isUniqueGenerated = false;

                while (!isUniqueGenerated)
                {
                    uniqueStr = GetUniqueKey();
                    try
                    {
                        uniqueHashTable.Add(uniqueStr, "");
                        isUniqueGenerated = true;
                    }
                    catch (Exception ex)
                    {
                        nonUniqueList.Add(uniqueStr);
                        // Non-unique generated
                    }
                }
            }

            uniqueList = uniqueHashTable.Keys.Cast<string>().ToList();

            return new Tuple<List<string>, List<string>>(uniqueList, nonUniqueList);
        }

        public static string GetUniqueKey()
        {
            int size = 7;
            char[] chars = new char[62];
            string a = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
            chars = a.ToCharArray();

            RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();

            byte[] data = new byte[size];
            crypto.GetNonZeroBytes(data);

            StringBuilder result = new StringBuilder(size);

            foreach (byte b in data)
                result.Append(chars[b % (chars.Length - 1)]);

            return Convert.ToString(result);
        }

下面的整个控制台应用程序代码;

    class Program
    {
        static string uniqueStr;
        static Stopwatch stopwatch;
        static bool isUniqueGenerated;
        static Hashtable uniqueHashTable;
        static List<string> uniqueList;
        static List<string> nonUniqueList;
        static Tuple<List<string>, List<string>> generatedTuple;

        static void Main(string[] args)
        {
            int i = 0, y = 0, count = 100000;

            while (i < 10 && y < 4)
            {
                stopwatch = new Stopwatch();

                stopwatch.Start();

                generatedTuple = GenerateUniqueList(count);

                stopwatch.Stop();

                Console.WriteLine("Time elapsed: {0} --- {1} Unique  --- {2} nonUnique",
                    stopwatch.Elapsed,
                    generatedTuple.Item1.Count().ToFormattedInt(),
                    generatedTuple.Item2.Count().ToFormattedInt());

                i++;
                if (i == 9)
                {
                    Console.WriteLine(string.Empty);
                    y++;
                    count *= 10;
                    i = 0;
                }
            }


            Console.ReadLine();
        }

        public static Tuple<List<string>, List<string>> GenerateUniqueList(int count)
        {
            uniqueHashTable = new Hashtable();
            nonUniqueList = new List<string>();
            uniqueList = new List<string>();

            for (int i = 0; i < count; i++)
            {
                isUniqueGenerated = false;

                while (!isUniqueGenerated)
                {
                    uniqueStr = GetUniqueKey();
                    try
                    {
                        uniqueHashTable.Add(uniqueStr, "");
                        isUniqueGenerated = true;
                    }
                    catch (Exception ex)
                    {
                        nonUniqueList.Add(uniqueStr);
                        // Non-unique generated
                    }
                }
            }

            uniqueList = uniqueHashTable.Keys.Cast<string>().ToList();

            return new Tuple<List<string>, List<string>>(uniqueList, nonUniqueList);
        }

        public static string GetUniqueKey()
        {
            int size = 7;
            char[] chars = new char[62];
            string a = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
            chars = a.ToCharArray();

            RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();

            byte[] data = new byte[size];
            crypto.GetNonZeroBytes(data);

            StringBuilder result = new StringBuilder(size);

            foreach (byte b in data)
                result.Append(chars[b % (chars.Length - 1)]);

            return Convert.ToString(result);
        }
    }

    public static class IntExtensions
    {
        public static string ToFormattedInt(this int value)
        {
            return string.Format(CultureInfo.InvariantCulture, "{0:0,0}", value);
        }
    }

答案 3 :(得分:0)

使用严格的字母数字字符将您绘制的池限制为62.使用完整的可打印字符集(ASCII 32-126)可将池增加到94,从而降低发生冲突的可能性并消除单独创建池。