如何生成LONG guid?

时间:2010-05-19 17:07:27

标签: c# c++ algorithm

我想生成一个很长的UUID - 类似于gmail使用的会话密钥。它应至少为256个字符且不超过512个字符。它可以包含所有字母数字字符和一些特殊字符(键盘上功能键下方的字符)。这已经完成了还是有样品?

C ++或C#

更新:GUID是不够的。我们已经看到了碰撞,需要解决这个问题。 512是迄今为止的最大值,因为它会阻止我们更改已经发货的东西。

更新2:对于那些坚持GUID独特性的人来说,如果有人想猜测你的下一个会话ID,他们就不必计算下一个万亿年的组合。他们所要做的只是限制时间因素,它们将在数小时内完成。

9 个答案:

答案 0 :(得分:28)

如果您的GUID发生碰撞,请问您是如何生成的?

GUID会因为基于以下原因而发生碰撞,这在天文学上是不可能的:

  • 60位 - 生成期间的时间戳
  • 48位 - 计算机标识符
  • 14位 - 唯一ID
  • 固定6位

您必须在同一台计算机上运行GUID生成约50次,以便有50%的碰撞几率。请注意,瞬间测量到纳秒。

更新

根据您的评论“将GUID放入哈希表”...... GetHashCode()方法导致冲突,而不是GUID:

public override int GetHashCode()
{
    return ((this._a ^ ((this._b << 0x10) | ((ushort) this._c))) ^ ((this._f << 0x18) | this._k));
}

您可以看到它返回int,因此如果哈希表中有超过2 ^ 32个“GUID”,那么您将100%发生冲突。

答案 1 :(得分:13)

根据你的更新2你是正确的Guids是可预测的甚至msdn引用。这是一种使用强大的随机数生成器来创建ID的方法。

static long counter; //store and load the counter from persistent storage every time the program loads or closes.

public static string CreateRandomString(int length)
{
    long count = System.Threading.Interlocked.Increment(ref counter);
    int PasswordLength = length;
    String _allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ23456789";
    Byte[] randomBytes = new Byte[PasswordLength];
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetBytes(randomBytes);
    char[] chars = new char[PasswordLength];
    int allowedCharCount = _allowedChars.Length;
    for (int i = 0; i < PasswordLength; i++)
    {
        while(randomBytes[i] > byte.MaxValue - (byte.MaxValue % allowedCharCount))
        {
            byte[] tmp = new byte[1];
            rng.GetBytes(tmp);
            randomBytes[i] = tmp[0];
        }
        chars[i] = _allowedChars[(int)randomBytes[i] % allowedCharCount];
    }
    byte[] buf = new byte[8];
    buf[0] = (byte) count;
    buf[1] = (byte) (count >> 8);
    buf[2] = (byte) (count >> 16);
    buf[3] = (byte) (count >> 24);
    buf[4] = (byte) (count >> 32);
    buf[5] = (byte) (count >> 40);
    buf[6] = (byte) (count >> 48);
    buf[7] = (byte) (count >> 56);
    return Convert.ToBase64String(buf) + new string(chars);
}

编辑我知道存在一些偏见,因为allowedCharCount不能被255整除,你可以摆脱抛弃的偏差并获得一个新的随机数,如果它落入无​​人 - 其余的土地。

EDIT2 - 这不保证是唯一的,您可以保持静态64位(或更高,如果需要)单调计数器将其编码为base46并将其作为id的前4-5个字符。< /秒>

更新 - 现在保证是唯一的

更新2:算法现在速度较慢但已移除偏差。

编辑:我刚刚运行了一个测试,我想让你知道ToBase64String可以返回非字母数字字符(例如1个编码到"AQAAAAAAAAA=")只是你知道。

新版本:

从这个页面上的Matt Dotson's answer开始,如果你不是那么担心键空间,你就可以这样做,并且运行得更快。

public static string CreateRandomString(int length)
{
    length -= 12; //12 digits are the counter
    if (length <= 0)
        throw new ArgumentOutOfRangeException("length");
    long count = System.Threading.Interlocked.Increment(ref counter);
    Byte[] randomBytes = new Byte[length * 3 / 4];
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetBytes(randomBytes);

    byte[] buf = new byte[8];
    buf[0] = (byte)count;
    buf[1] = (byte)(count >> 8);
    buf[2] = (byte)(count >> 16);
    buf[3] = (byte)(count >> 24);
    buf[4] = (byte)(count >> 32);
    buf[5] = (byte)(count >> 40);
    buf[6] = (byte)(count >> 48);
    buf[7] = (byte)(count >> 56);
    return Convert.ToBase64String(buf) + Convert.ToBase64String(randomBytes);
}

答案 2 :(得分:10)

StringBuilder sb = new StringBuilder();
for (int i = 0; i < HOW_MUCH_YOU_WANT / 32; i++)
   sb.Append(Guid.NewGuid().ToString("N"));
return sb.ToString();

但是为了什么?

答案 3 :(得分:8)

这里的问题是为什么,而不是如何。会话ID 大于比GUID没用,因为它已经足够大,可以阻止暴力攻击。

如果您担心预测GUID,请不要。与早期的顺序GUID不同,V4 GUID基于RC4是加密安全的。我所知道的唯一漏洞取决于对生成值的进程的内部状态的完全访问权限,因此如果您拥有的是GUID的部分序列,它就无法到达任何地方。

如果您是偏执狂,请生成GUID,使用SHA-1等方法对其进行哈希处理,然后使用该值。但是,这是浪费时间。如果你担心会话劫持,你应该看看SSL,而不是这个。

答案 4 :(得分:3)

byte[] random = new Byte[384];

//RNGCryptoServiceProvider is an implementation of a random number generator.
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(random);
var sessionId = Convert.ToBase64String(random);

您可以将base64编码中的“/”和“=”替换为您可以接受的任何特殊字符。

Base64编码创建一个比字节数组大4/3的字符串(因此384字节应该给你512个字符)。

这应该给你的命令比base16(hex)编码的guid更多的值。 512 ^ 16 vs 512 ^ 64

此外,如果您将这些放在sql server中,请确保关闭不区分大小写。

答案 5 :(得分:1)

有两种非常简单的方法(C#):

1)使用Guid.NewGuid()生成一堆Guids。ToString(“N”)。每个GUID长度为32个字符,因此只需生成8个字符并将它们连接起来就可以得到256个字符。

2)在你的UID中创建一个你想要的可接受字符的常量字符串(const string sChars =“abcdef”)。然后在循环中,通过随机生成一个从0到可接受字符串(sChars)长度的数字,从该字符串中随机选择字符,并将它们连接在一个新字符串中(使用stringbuilder使其更具性能,但字符串将也工作)。

答案 6 :(得分:1)

您可能需要查看提升Uuid Library。它支持各种发生器,包括可能满足您需求的随机发生器。

答案 7 :(得分:0)

我会使用某种std :: time()的哈希值sha512。 ex(使用crypto ++进行sha hash + base64编码)。

#include <iostream>
#include <sstream>
#include <ctime>
#include <crypto++/sha.h>
#include <crypto++/base64.h>

int main() {
    std::string digest;
    std::stringstream ss("");
    ss << std::time(NULL);

    // borrowed from http://www.cryptopp.com/fom-serve/cache/50.html
    CryptoPP::SHA512 hash;
    CryptoPP::StringSource foo(ss.str(), true,
        new CryptoPP::HashFilter(hash,
           new CryptoPP::Base64Encoder(
               new CryptoPP::StringSink(digest))));
    std::cout << digest << std::endl;

    return 0;
}

答案 8 :(得分:0)

https://github.com/bigfatsea/SUID简单唯一标识符

虽然它在Java中,但可以轻松移植到任何其他语言。 136年后,您可能会在同一个实例上看到重复的ID,这对于中小型项目来说已经足够了。

示例:

long id = SUID.id().get();