I want to create a cryptographically secure GUID (v4) in .NET.
.NET's Guid.NewGuid()
function is not cryptographically secure, but .NET does provide the System.Security.Cryptography.RNGCryptoServiceProvider
class.
I would like to be able to pass a random number function as a delegate to Guid.NewGuid
(or even pass some class that provides a generator interface) but it doesn't look as though that is possible with the default implementation.
Can I create a cryptographically secure GUID by using System.GUID
and System.Security.Cryptography.RNGCryptoServiceProvider
together?
Yes you can, Guid allows you to create a Guid using a byte array, and RNGCryptoServiceProvider can generate a random byte array, so you can use the output to feed a new Guid:
public Guid CreateCryptographicallySecureGuid()
{
using (var provider = new RNGCryptoServiceProvider())
{
var bytes = new byte[16];
provider.GetBytes(bytes);
return new Guid(bytes);
}
}
Read Brad M's answer below: https://stackoverflow.com/a/54132397/113535
If anyone is interested here is the above sample code adjusted for .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 says there are a few bits that should be fixed in order to indicate that this GUID is a version-4 (random) one. Here is the code altered to set/unset these 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);
}
}
If you are using at least c# 7.2 and netcoreapp2.1 (or System.Memory
), this is the fastest/most efficient approach.
public static Guid CreateCryptographicallySecureGuid()
{
Span<byte> bytes = stackalloc byte[16];
RandomNumberGenerator.Fill(bytes);
return new Guid(bytes);
}
I created a benchmark comparing this to the accepted answer. I modified it to use a static implementation of RandomNumberGenerator
since GetBytes()
is thread safe. (although the only guarantee I see is that RNGCryptoServiceProvider
has a thread safe implementation...it's possible other implementations do not)
[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 | - | - | - | - |
2020 Modified Version
I found @rlamoni's answer to be great. Just needs a little bit modifications in its bitwise operations to correctly reflect GUID version 4 identifying bits.
To be more specific about the corrections in my answer:
- The modified bytes should be 7th and 9th.
- The bitwise-and operands have been corrected.
Update
As the user Benrobot pointed out, Guid was invalid. I suppose because of his device Endianness being different to mine.
This prompted me to further enhance my answer. In addition to the two corrections I pointed out above in my original answer, here are few more enhancements of my answer:
- Accounted for Endianness of the current computer architecture.
- Added self-explanatory constant & variable identifiers instead of constant literals.
- Used simple
using statement
that doesn't require braces.
- Added some comments and required
using directive
.
using System;
using System.Security.Cryptography;
/// Generates random Guid.
///
/// Characteristics
/// 1. Variant: RFC 4122
/// 2. Version: 4
/// RFC
/// https://tools.ietf.org/html/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);
}
Online validators
To check for the validity of the generated GUID:
- http://guid.one/parse
- https://www.beautifyconverter.com/uuid-validator.php
- https://www.freecodeformat.com/validate-uuid-guid.php
- http://guid.us/Test/GUID
References
- RFC4122 Version's Section.
- GuidOne, an underrated GUID library.
- This GUID guide.
- Cryptosys
Uuid.cs
C# class.