在 .NET 中创建加密安全随机 GUID
Create a cryptographically secure random GUID in .NET
我想在 .NET 中创建加密安全 GUID (v4)。
.NET 的 Guid.NewGuid()
函数不是加密安全的,但 .NET 确实提供了 System.Security.Cryptography.RNGCryptoServiceProvider
class.
我希望能够将随机数函数作为委托传递给 Guid.NewGuid
(或者甚至传递一些提供生成器接口的 class),但它看起来不像这可以通过默认实现实现。
我可以通过同时使用 System.GUID
和 System.Security.Cryptography.RNGCryptoServiceProvider
创建加密安全的 GUID 吗?
是的,Guid allows you to create a Guid using a byte array, and RNGCryptoServiceProvider 可以生成随机字节数组,因此您可以使用输出来提供新的 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) 调整的上述示例代码
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = System.Security.Cryptography.RandomNumberGenerator.Create())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
https://www.rfc-editor.org/rfc/rfc4122 说有一些位应该被修复以表明这个 GUID 是第 4 版(随机)。这是更改为 set/unset 这些位的代码。
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
),这是 fastest/most 有效的方法。
public static Guid CreateCryptographicallySecureGuid()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
return new Guid(bytes);
}
我创建了一个基准,将其与已接受的答案进行比较。我将其修改为使用 RandomNumberGenerator
的静态实现,因为 GetBytes()
是线程安全的。 (虽然我看到的唯一保证是 RNGCryptoServiceProvider
有一个线程安全的实现......其他实现可能没有)
[MemoryDiagnoser]
public class Test
{
private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
[Benchmark]
public void Heap()
{
var bytes = new byte[16];
_rng.GetBytes(bytes);
new Guid(bytes);
}
[Benchmark]
public void Fill()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
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 | - | - | - | - |
2020年修改版
我发现@rlamoni 的 很棒。只需要对其按位运算进行一点修改即可正确反映 GUID 版本 4 标识位,以说明大端和小端架构。
更具体地说明我的回答中的更正:
- 修改的字节应该是第7和第9。
- 按位与操作数已更正。
更新
作为用户 Benrobot pointed out, Guid was invalid. I suppose because of his device Endianness 与我不同。
这促使我进一步完善我的回答。除了上面我在原始答案中指出的两个更正之外,这里还有一些对我的答案的改进:
- 占当前计算机体系结构的Endianness。
- 添加了不言自明的常量和变量标识符,而不是常量文字。
- 使用了不需要大括号的 simple
using statement
。
- 添加了一些评论并要求
using directive
。
using System;
using System.Security.Cryptography;
/// Generates a cryptographically secure random Guid.
///
/// Characteristics
/// - Variant: RFC 4122
/// - Version: 4
/// RFC
/// https://www.rfc-editor.org/rfc/rfc4122#section-4.1.3
/// Whosebug
///
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);
}
在线验证者
检查生成的 GUID 的有效性:
- http://guid.one/parse
- https://www.beautifyconverter.com/uuid-validator.php
- https://www.freecodeformat.com/validate-uuid-guid.php
- http://guid.us/Test/GUID
参考资料
太棒了。但我想针对 .NET 5.0 对其进行优化,它不需要 RNG 加密提供程序。我的 .NET 5.0 修改版本如下:
using System;
using System.Security.Cryptography;
/// Generates a cryptographically secure random Guid.
///
/// Characteristics
/// - GUID Variant: RFC 4122
/// - GUID Version: 4
/// - .NET 5
/// RFC
/// https://tools.ietf.org/html/rfc4122#section-4.1.3
/// Whosebug
///
static Guid CreateCryptographicallySecureRandomRFC4122Guid()
{
// 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];
RandomNumberGenerator.Fill(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);
}
此处编辑使用 RandomNumberGenerator
(Docs), Fill
方法执行以下操作:
Fills a span with cryptographically strong random bytes
我想在 .NET 中创建加密安全 GUID (v4)。
.NET 的 Guid.NewGuid()
函数不是加密安全的,但 .NET 确实提供了 System.Security.Cryptography.RNGCryptoServiceProvider
class.
我希望能够将随机数函数作为委托传递给 Guid.NewGuid
(或者甚至传递一些提供生成器接口的 class),但它看起来不像这可以通过默认实现实现。
我可以通过同时使用 System.GUID
和 System.Security.Cryptography.RNGCryptoServiceProvider
创建加密安全的 GUID 吗?
是的,Guid allows you to create a Guid using a byte array, and RNGCryptoServiceProvider 可以生成随机字节数组,因此您可以使用输出来提供新的 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) 调整的上述示例代码
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = System.Security.Cryptography.RandomNumberGenerator.Create())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
https://www.rfc-editor.org/rfc/rfc4122 说有一些位应该被修复以表明这个 GUID 是第 4 版(随机)。这是更改为 set/unset 这些位的代码。
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
),这是 fastest/most 有效的方法。
public static Guid CreateCryptographicallySecureGuid()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
return new Guid(bytes);
}
我创建了一个基准,将其与已接受的答案进行比较。我将其修改为使用 RandomNumberGenerator
的静态实现,因为 GetBytes()
是线程安全的。 (虽然我看到的唯一保证是 RNGCryptoServiceProvider
有一个线程安全的实现......其他实现可能没有)
[MemoryDiagnoser]
public class Test
{
private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
[Benchmark]
public void Heap()
{
var bytes = new byte[16];
_rng.GetBytes(bytes);
new Guid(bytes);
}
[Benchmark]
public void Fill()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
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 | - | - | - | - |
2020年修改版
我发现@rlamoni 的
更具体地说明我的回答中的更正:
- 修改的字节应该是第7和第9。
- 按位与操作数已更正。
更新
作为用户 Benrobot pointed out, Guid was invalid. I suppose because of his device Endianness 与我不同。
这促使我进一步完善我的回答。除了上面我在原始答案中指出的两个更正之外,这里还有一些对我的答案的改进:
- 占当前计算机体系结构的Endianness。
- 添加了不言自明的常量和变量标识符,而不是常量文字。
- 使用了不需要大括号的 simple
using statement
。 - 添加了一些评论并要求
using directive
。
using System;
using System.Security.Cryptography;
/// Generates a cryptographically secure random Guid.
///
/// Characteristics
/// - Variant: RFC 4122
/// - Version: 4
/// RFC
/// https://www.rfc-editor.org/rfc/rfc4122#section-4.1.3
/// Whosebug
///
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);
}
在线验证者
检查生成的 GUID 的有效性:
- http://guid.one/parse
- https://www.beautifyconverter.com/uuid-validator.php
- https://www.freecodeformat.com/validate-uuid-guid.php
- http://guid.us/Test/GUID
参考资料
using System;
using System.Security.Cryptography;
/// Generates a cryptographically secure random Guid.
///
/// Characteristics
/// - GUID Variant: RFC 4122
/// - GUID Version: 4
/// - .NET 5
/// RFC
/// https://tools.ietf.org/html/rfc4122#section-4.1.3
/// Whosebug
///
static Guid CreateCryptographicallySecureRandomRFC4122Guid()
{
// 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];
RandomNumberGenerator.Fill(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);
}
此处编辑使用 RandomNumberGenerator
(Docs), Fill
方法执行以下操作:
Fills a span with cryptographically strong random bytes