Создайте криптографически безопасный случайный GUID в .NET.
Я хочу создать криптографически безопасный GUID (v4) в .NET.
Функция .NET Guid.NewGuid()
не является криптографически безопасной, но .NET предоставляет класс System.Security.Cryptography.RNGCryptoServiceProvider
.
Я хотел бы иметь возможность передавать функцию случайного числа в качестве делегата в Guid.NewGuid
(или даже передавать некоторый класс, предоставляющий интерфейс генератора), но это не выглядит так, как это возможно при реализации по умолчанию.
Можно ли создать криптографически безопасный GUID с помощью System.GUID
и System.Security.Cryptography.RNGCryptoServiceProvider
вместе?
Ответы
Ответ 1
Да, вы можете, Guid, вы можете создать Guid с использованием массива байтов и RNGCryptoServiceProvider может генерировать случайный массив байтов, поэтому вы можете использовать вывод для подачи нового руководства:
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
Ответ 2
Если вам интересен приведенный выше примерный код, скорректированный для .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);
}
}
Ответ 3
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);
}
}
Ответ 4
Если вы используете как минимум С# 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
имеет поточно- 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 | - | - | - | - |