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

jyztefdp  于 2022-12-27  发布在  .NET
关注(0)|答案(6)|浏览(203)

我想在. NET中创建加密安全GUID(v4)。
.NET的Guid.NewGuid()函数在加密方面并不安全,但.NET确实提供了System.Security.Cryptography.RNGCryptoServiceProvider类。
我希望能够将一个随机数函数作为委托传递给Guid.NewGuid(或者甚至传递一些提供生成器接口的类),但看起来这在默认实现中是不可能的。
我可以通过同时使用System.GUIDSystem.Security.Cryptography.RNGCryptoServiceProvider来创建加密安全的GUID吗?

pw136qt2

pw136qt21#

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

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

        return new Guid(bytes);
    }
}
chhqkbe1

chhqkbe12#

阅读下面布拉德M的回答:https://stackoverflow.com/a/54132397/113535
如果有人感兴趣,这里是上述示例代码调整为.NET核心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);
    }
}
gblwokeq

gblwokeq3#

https://www.rfc-editor.org/rfc/rfc4122表示有几个位需要修改,以指示此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);
    }
}
3hvapo4f

3hvapo4f4#

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

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

我创建了一个基准测试,将其与公认的答案进行比较。我修改它以使用RandomNumberGenerator的静态实现,因为GetBytes()是线程安全的。(尽管我看到的唯一保证是RNGCryptoServiceProvider具有线程安全的实现......但其他实现可能没有)
x一个一个一个一个x一个一个二个x

biswetbf

biswetbf5#

2020修订版

我发现@rlamoni的answer非常棒,只是需要在它的位操作中做一点修改,以正确地反映GUID版本4的标识位,从而同时考虑到Big Endian和Little Endian架构。
更具体地说,我的答复中的更正如下:
1.修改的字节应为第7和第9个。
1.已更正按位与操作数。

更新

正如用户Benrobot所指出的,guid是无效的。我想是因为他的设备Endianness与我的不同。
这促使我进一步完善我的答案。除了我在上面的原始答案中指出的两处更正外,这里还有几处对我的答案的完善:
1.占当前计算机体系结构的Endianness
1.添加了自解释常量和变量标识符,而不是常量文字。
1.使用不需要大括号的simpleusing statement
1.添加了一些注解并要求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
/// 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);
}

在线验证器

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

参考文献

  • RFC4122版本章节。
  • GuidOne,一个被低估的GUID库。
  • This GUID指南。
  • 密码系统Uuid.cs C#class.
p3rjfoxz

p3rjfoxz6#

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

这里的编辑使用RandomNumberGeneratorDocs),Fill方法执行以下操作:
用加密的强随机字节填充范围

相关问题