Ich möchte ein kryptografisch sicheres GUID (v4) in .NET erstellen.
Die Guid.NewGuid()
-Funktion von .NET ist nicht kryptographisch sicher, aber .NET stellt die System.Security.Cryptography.RNGCryptoServiceProvider
-Klasse bereit.
Ich möchte gerne eine Zufallszahlenfunktion als Delegat an Guid.NewGuid
übergeben (oder sogar eine Klasse übergeben, die eine Generatorschnittstelle bereitstellt), aber es sieht nicht so aus, als wäre dies mit der Standardimplementierung möglich.
Kann ich ein kryptografisch sicheres GUID erstellen, indem Sie System.GUID
und System.Security.Cryptography.RNGCryptoServiceProvider
zusammen verwenden?
Ja, Sie können mit Guid eine Guid mit einem Byte-Array erstellen, und RNGCryptoServiceProvider kann ein zufälliges Byte-Array generieren, sodass Sie die Ausgabe verwenden können, um eine neue Guid zu füttern:
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
Wenn jemand interessiert ist, ist der obige Beispielcode für .NET Core 1.0 (DNX) angepasst.
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = System.Security.Cryptography.RandomNumberGenerator.Create())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
https://tools.ietf.org/html/rfc4122 sagt, es gibt ein paar Bits, die korrigiert werden sollten, um anzuzeigen, dass es sich bei GUID um eine Version 4 (zufällig) handelt. Hier ist der Code geändert, um diese Bits zu setzen/zu setzen.
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);
}
}
Wenn Sie mindestens c # 7.2 und netcoreapp2.1 (oder System.Memory
) verwenden, ist dies der schnellste/effizienteste Ansatz.
public static Guid CreateCryptographicallySecureGuid()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
return new Guid(bytes);
}
Ich habe einen Benchmark erstellt, der dies mit der akzeptierten Antwort vergleicht. Ich habe es geändert, um eine statische Implementierung von RandomNumberGenerator
zu verwenden, da GetBytes()
threadsicher ist. (obwohl die einzige Garantie, die ich sehe, ist, dass RNGCryptoServiceProvider
eine Thread-sichere Implementierung hat ... es ist möglich, dass andere Implementierungen dies nicht tun)
[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 | - | - | - | - |