Je souhaite créer un GUID (v4) cryptographiquement sécurisé dans .NET.
La fonction Guid.NewGuid()
de .NET n'est pas sécurisée sur le plan cryptographique, mais .NET fournit la classe System.Security.Cryptography.RNGCryptoServiceProvider
.
J'aimerais pouvoir transmettre une fonction de nombre aléatoire en tant que délégué à Guid.NewGuid
(ou même transmettre une classe fournissant une interface générateur), mais cela ne semble pas possible avec l'implémentation par défaut.
Puis-je créer un GUID sécurisé sur le plan cryptographique en utilisant System.GUID
et System.Security.Cryptography.RNGCryptoServiceProvider
ensemble?
Oui, vous pouvez, Guid vous permettre de créer un Guid en utilisant un tableau d'octets, et RNGCryptoServiceProvider peut générer un tableau d'octets aléatoire. Vous pouvez donc utiliser la sortie pour alimenter un nouveau Guid:
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
Si vous êtes intéressé, voici l'exemple de code ci-dessus, ajusté pour .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://tools.ietf.org/html/rfc4122 indique que quelques bits doivent être corrigés afin d'indiquer que ce GUID est un fichier de version 4 (aléatoire). Voici le code modifié pour activer/désactiver ces bits.
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);
}
}
Si vous utilisez au moins c # 7.2 et netcoreapp2.1 (ou System.Memory
), il s'agit de l'approche la plus rapide/la plus efficace.
public static Guid CreateCryptographicallySecureGuid()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
return new Guid(bytes);
}
J'ai créé un repère comparant cela à la réponse acceptée. Je l'ai modifié pour utiliser une implémentation statique de RandomNumberGenerator
car GetBytes()
est thread-safe. (Bien que la seule garantie que je voie est que RNGCryptoServiceProvider
a une implémentation thread-safe ... il est possible que d'autres implémentations n'en disposent pas)
[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 | - | - | - | - |