在.NET中创建加密安全随机GUID

jed*_*ung 24 .net c# security guid

我想在.NET中创建一个加密安全的GUID(v4).

.NET的Guid.NewGuid()功能不是加密安全的,但.NET确实提供了System.Security.Cryptography.RNGCryptoServiceProvider类.

我希望能够将随机数函数作为委托Guid.NewGuid传递给(或者甚至传递一些提供生成器接口的类),但它看起来并不像默认实现那样.

我可以创建通过使用密码安全GUID System.GUIDSystem.Security.Cryptography.RNGCryptoServiceProvider在一起?

Gus*_*man 40

是的,Guid允许您使用字节数组创建Guid,而RNGCryptoServiceProvider可以生成随机字节数组,因此您可以使用输出来提供新的Guid:

public Guid CreateCryptographicallySecureGuid() 
{
    using (var provider = new RNGCryptoServiceProvider()) 
    {
        var bytes = new byte[16];
        provider.GetBytes(bytes);

        return new Guid(bytes);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 等等,真的这么简单吗?我认为有一个 GUID 标准 - 前几个字节不是定义使用的版本,而最后几个字节定义其他东西吗?我认为 GUID 不仅仅是随机字节,因此问题。 (2认同)
  • 加密安全 GUID 和标准化 Guid 之间存在差异,如果您希望 Guid 避免冲突,则使用 Guid.NewGuid,否则使用它。任何标准化的 Guid 都不是加密安全的,因为正如您所说的那样有生成它们的规则,那么它的强度会非常低(如果您知道 16 个字节中的 8 个是如何生成的,则只需要对这 8 个字节进行暴力破解即可。 .) (2认同)
  • @Gusman 就其价值而言,`RNGCryptoServiceProvider` 实现了`IDisposable`,因此它应该包含在`using` 语句或`try/finally` 中。偶然发现这个问题的未来读者可能会发现这很有用。 (2认同)

Kan*_*ane 14

如果有人对此感兴趣,请参阅上面针对.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);
    }
}
Run Code Online (Sandbox Code Playgroud)


om-*_*-ha 8

2020年修改版

我发现@rlamoni 的回答很棒。只需要在其按位运算中稍作修改即可正确反映 GUID 版本 4 识别位,以解决 Big Endian 和 Little Endian 架构。

更具体地说明我的答案中的更正:

  1. 修改后的字节应该是第 7 个和第 9 个。
  2. 按位和操作数已得到纠正。

更新

正如用户Benrobot 所指出的,Guid 是无效的。我想是因为他的设备Endianness与我的不同。

这促使我进一步加强我的回答。除了我在上面的原始答案中指出的两个更正之外,这里还有一些我的答案的改进:

  1. 字节序当前计算机体系结构。
  2. 添加了不言自明的常量和变量标识符,而不是常量文字。
  3. 使用简单的 using statement不需要大括号。
  4. 添加了一些注释和必需的using directive.
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
///     /sf/answers/4160625311/
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);
}
Run Code Online (Sandbox Code Playgroud)

在线验证器

要检查生成的 GUID 的有效性:

参考

  • RFC4122 版本的部分
  • GuidOne,一个被低估的 GUID 库。
  • GUID 指南。
  • Cryptosys Uuid.csC#


Mic*_*ini 7

om-ha 的回答非常棒。但我想针对 .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
/// Stackoverflow
///     https://stackoverflow.com/a/59437504/10830091
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 -- 8th byte
    bytes[variantByteIndex] = (byte)(bytes[variantByteIndex] & variantMask | variantShift);

    // Initialize Guid from the modified random bytes
    return new Guid(bytes);
}
Run Code Online (Sandbox Code Playgroud)

此处编辑使用RandomNumberGenerator文档),Fill方法执行以下操作:

用加密的强随机字节填充跨度


rla*_*oni 5

https://tools.ietf.org/html/rfc4122表示有一些地方应该修复,以表明此GUID是版本4(随机)。这是为设置/取消设置这些位而修改的代码。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果审核员或其他IT安全专家查看生成的用于安全Id编号的GUID,则他/她将想知道所有这些编号是否都是难以猜测的类型。如果您将整个GUID随机化(而不是设置版本),则创建的某些GUID看起来将是类型1或其他一些不安全的类型,即使它们不是。因此,将版本设置为最合适的值将有助于避免某些混乱。我怀疑如果您忽略该标准,任何代码都会停止工作。但是,为了清楚起见,最好遵守。 (2认同)

Bra*_*d M 5

如果您至少使用c#7.2和netcoreapp2.1(或System.Memory),则这是最快/最有效的方法。

public static Guid CreateCryptographicallySecureGuid()
{
    Span<byte> bytes = stackalloc byte[16];
    RandomNumberGenerator.Fill(bytes);
    return new Guid(bytes);
}
Run Code Online (Sandbox Code Playgroud)

我创建了一个基准,将其与接受的答案进行比较。我修改了它以使用静态实现,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);
    }
}
Run Code Online (Sandbox Code Playgroud)
| 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 |           - |           - |           - |                   - |
Run Code Online (Sandbox Code Playgroud)