web-dev-qa-db-fra.com

Créer un hasard cryptographiquement sécurisé GUID en .NET

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?

20
jedd.ahyoung

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

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

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

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 |           - |           - |           - |                   - |
1
Brad M