C# 在.NET中创建加密安全的随机GUID
我想在.NET中创建一个加密安全的GUID(v4) .NET的C# 在.NET中创建加密安全的随机GUID,c#,.net,security,guid,C#,.net,Security,Guid,我想在.NET中创建一个加密安全的GUID(v4) .NET的Guid.NewGuid()函数在加密方面不安全,但.NET确实提供了System.Security.Cryptography.RNGCryptoServiceProvider类 我希望能够将一个随机数函数作为委托传递给Guid.NewGuid(或者甚至传递一些提供生成器接口的类),但在默认实现中,这似乎是不可能的 我可以一起使用System.GUID和System.Security.Cryptography.RNGCryptoSer
Guid.NewGuid()
函数在加密方面不安全,但.NET确实提供了System.Security.Cryptography.RNGCryptoServiceProvider
类
我希望能够将一个随机数函数作为委托传递给Guid.NewGuid
(或者甚至传递一些提供生成器接口的类),但在默认实现中,这似乎是不可能的
我可以一起使用System.GUID
和System.Security.Cryptography.RNGCryptoServiceProvider
创建加密安全的GUID吗?可以,允许您使用字节数组创建GUID,并可以生成随机字节数组,因此您可以使用输出来提供新的GUID:
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
请阅读以下Brad M的答案: 如果有人对此感兴趣,以上示例代码是否针对.NET Core 1.0(DNX)进行了调整 表示应该固定一些位,以指示此GUID是版本4(随机)GUID。这是更改为设置/取消设置这些位的代码
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
bytes[8] = (byte)(bytes[8] & 0xBF | 0x80);
bytes[7] = (byte)(bytes[7] & 0x4F | 0x40);
return new Guid(bytes);
}
}
如果您至少使用c#7.2和netcoreapp2.1(或
System.Memory
),这是最快/最有效的方法
公共静态Guid CreateCryptographicalySecureGUID()
{
Span字节=stackalloc字节[16];
RandomNumberGenerator.Fill(字节);
返回新的Guid(字节);
}
我创建了一个基准,将其与公认的答案进行比较。我对它进行了修改,使用了RandomNumberGenerator
的静态实现,因为GetBytes()
是线程安全的。(虽然我看到的唯一保证是RNGCryptoServiceProvider
有线程安全的实现……但其他实现可能没有)
[MemoryDiagnoser]
公开课考试
{
私有静态只读RandomNumberGenerator\u rng=RandomNumberGenerator.Create();
[基准]
公共空堆()
{
var字节=新字节[16];
_rng.GetBytes(字节);
新Guid(字节);
}
[基准]
填空
{
Span字节=stackalloc字节[16];
RandomNumberGenerator.Fill(字节);
新Guid(字节);
}
}
2020年修订版
我发现@rlamoni's很棒。只需在其逐位操作中稍作修改,即可正确反映GUID版本4的标识位,从而兼顾大端和小端体系结构
更具体地说,我的回答中的更正:
- RFC4122版本的
- ,一个被低估的GUID库
- 指南
- Cryptosys
C#Uuid.cs
使用系统;
使用System.Security.Cryptography;
///生成加密安全的随机Guid。
///
///特征
///-变体:RFC 4122
///-版本:4
///RFC
/// https://tools.ietf.org/html/rfc4122#section-4.1.3
///堆栈溢出
/// https://stackoverflow.com/a/59437504/10830091
静态Guid CreateCryptographicalySecureRandomRFC4122GUID()
{
//字节索引
int versionByteIndex=BitConverter.IsLittleEndian?7:6;
const int variantByteIndex=8;
//'Version 4'的版本掩码和移位`
const int versionMask=0x0F;
const int versionShift=0x40;
//RFC 4122的变体掩码和移位`
常数int variantMask=0x3F;
常量整型变量移位=0x80;
//获取加密强随机值的字节
var字节=新字节[16];
RandomNumberGenerator.Fill(字节);
//设置版本位——第6或第7字节,分别根据尾数、大尾数或小尾数设置
字节[versionByteIndex]=(字节)(字节[versionByteIndex]&版本任务|版本移位);
//设置变量位——第9字节
字节[variantByteIndex]=(字节)(字节[variantByteIndex]和variantMask | variantShift);
//从修改的随机字节初始化Guid
返回新的Guid(字节);
}
等等,真的这么简单吗?我认为有一个GUID标准——前几个字节不是定义了使用的版本吗,而最后几个字节定义了其他东西吗?我认为GUID不仅仅是随机字节,因此问题就来了。加密安全GUID和标准化GUID之间有区别,如果希望GUID避免冲突,请使用GUID.NewGuid,否则请使用它。任何标准化的Guid在加密方面都是不安全的,因为正如您正确地说的,有规则生成它们,那么它的强度将非常低(如果您知道16个字节中的8个是如何生成的,您只需要强制执行这8个字节…)嗯,冲突规则是为了避免机器之间的冲突,如果我没记错的话,它认为在同一台机器上,这些guid之间发生冲突的几率要比标准化guid小得多,获得副本的几率在2^128之间,因此我们的太阳在我们脸上爆炸的可能性比标准化guid大得多
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
bytes[8] = (byte)(bytes[8] & 0xBF | 0x80);
bytes[7] = (byte)(bytes[7] & 0x4F | 0x40);
return new Guid(bytes);
}
}
| Method | Mean | Error | StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
|------- |---------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
| Heap | 129.4 ns | 0.3074 ns | 0.2725 ns | 0.0093 | - | - | 40 B |
| Fill | 116.5 ns | 0.3440 ns | 0.2872 ns | - | - | - | - |
using System;
using System.Security.Cryptography;
/// Generates a cryptographically secure random Guid.
///
/// Characteristics
/// - Variant: RFC 4122
/// - Version: 4
/// RFC
/// https://tools.ietf.org/html/rfc4122#section-4.1.3
/// Stackoverflow
/// https://stackoverflow.com/a/59437504/10830091
static Guid CreateCryptographicallySecureRandomRFC4122Guid()
{
using var cryptoProvider = new RNGCryptoServiceProvider();
// byte indices
int versionByteIndex = BitConverter.IsLittleEndian ? 7 : 6;
const int variantByteIndex = 8;
// version mask & shift for `Version 4`
const int versionMask = 0x0F;
const int versionShift = 0x40;
// variant mask & shift for `RFC 4122`
const int variantMask = 0x3F;
const int variantShift = 0x80;
// get bytes of cryptographically-strong random values
var bytes = new byte[16];
cryptoProvider.GetBytes(bytes);
// Set version bits -- 6th or 7th byte according to Endianness, big or little Endian respectively
bytes[versionByteIndex] = (byte)(bytes[versionByteIndex] & versionMask | versionShift);
// Set variant bits -- 9th byte
bytes[variantByteIndex] = (byte)(bytes[variantByteIndex] & variantMask | variantShift);
// Initialize Guid from the modified random bytes
return new Guid(bytes);
}