如何生成LONG guid?

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

C ++或C#

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

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

根据你的更新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在本页的回答 ,如果你不是那么担心键空间,你可以这样做,它会更快地运行。

 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); } 

如果您的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%发生冲突。

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

但是为了什么?

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

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

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

 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中,请确保关闭不区分大小写。

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

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

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

您可以查看boost的Uuid Library 。 它支持各种发生器,包括可能适合您需求的随机发生器。

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

 #include  #include  #include  #include  #include  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; } 

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

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

例:

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