Intro
This article describes how to uniquely identify a machine on which you application is installed using a Universally Unique Identifier(UUID), in this article we will specifically look at Windows operating systems and use Microsoft's Globally Unique Identifiers (GUIDs). Sometimes you will need to be able to uniquely identify machines on which your applications are installed.
Windows MachineGUID
In the Windows registry is a key called MachineGUID that has a UUID which is created by Windows during installation and should in theory be unique to the machine. In practice this is not the case and I have often come across duplicates of this ID between machines. Below is a C#.Net code snippet showing how to read this value from the registry.
Guid guidMachineGUID;
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography") != null)
{
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography").GetValue("MachineGuid") != null)
{
// Get the stored value
guidMachineGUID = new Guid(Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography").GetValue("MachineGuid").ToString());
}
}
Creating you own MachineGUID
Since we cannot rely upon the MachineGUID provided for us by Windows we will need to create our own and store it in the registry.
We can leverage a Windows API function to generate GUIDs for us, namely UuidCreate. There is another function called UuidCreateSequential which can also be used but since this includes the MAC address of a machine's Ethernet card it could present a security risk if the GUID is stored insecurely or transmitted off the machine. So we will use UuidCreate which does also use the MAC address of a machine's Ethernet card but the MAC address cannot be read from the GUID, this function has three possible return values.
Return values of UuidCreate | RPC_S_OK | This UUID was correctly generated and will be unique across all computers (there does remain a small chance of duplicates but the odds against this are astronomical in scale). |
| RPC_S_OUT_OF_MEMORY | An error occurred creating the UUID. |
| RPC_S_UUID_LOCAL_ONLY | The UUID that was created is guaranteed to be unique to this computer only. |
| RPC_S_UUID_NO_ADDRESS | Cannot get Ethernet or token-ring hardware address for this computer, which means that the returned UUID is much more unlikely to be be unique across all computers. |
The following C#.Net code snippet gives an example on how to include the API call and create a method to return the generated GUID.
// ==========================================================
// GUID Creation
// ==========================================================
private const int RPC_S_OK = 0;
private const int RPC_S_OUT_OF_MEMORY = 14;
private const int RPC_S_UUID_NO_ADDRESS = 1739;
private const int RPC_S_UUID_LOCAL_ONLY = 1824;
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);
[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreate(out Guid guid);
/// <summary>
/// Creates the machine GUID.
/// </summary>
private static Guid mGetNewGUID()
{
Guid guidMachineGUID;
int intReturnValue = UuidCreate(out guidMachineGUID);
switch (intReturnValue)
{
case RPC_S_OK:
return guidMachineGUID;
break;
case RPC_S_OUT_OF_MEMORY:
throw new Exception("UuidCreate returned RPC_S_OUT_OF_MEMORY");
case RPC_S_UUID_NO_ADDRESS:
throw new Exception("UuidCreate returned RPC_S_UUID_NO_ADDRESS");
case RPC_S_UUID_LOCAL_ONLY:
throw new Exception("UuidCreate returned RPC_S_UUID_LOCAL_ONLY");
default:
throw new Exception("UuidCreate returned an un expected value = " + intReturnValue.ToString());
}
}
Storing your MachineGUID
The final thing to do is to store an access this UUID somewhere on the computer. Ideally you will want this UUID to remain the same for all your applications and across all users so a good location would be under the LocalMachine registry (HKEY_LOCAL_MACHINE).
To write values to the HKEY_LOCAL_MACHINE you will need to have administrator rights but not to read from it. Since you will not want your application to run under administrator rights and you only need to generate this UUID once it is a good idea to add your generation code to your installation code, if you were using a MSI for example you could add this code to a Custom Action.
I would suggest a location such as "HKEY_LOCAL_MACHINE\SOFTWARE\[Manufacturer]\Cryptography\MachineGuid" where you replace the [Manufacturer] with the name of your company.
The following C#.Net code snippet gives an example on how to store this value in registry.
/// <summary>
/// Creates the machine GUID.
/// Check for and create the MachineGUID registry key used to uniquely identify this machine
/// </summary>
private static bool mCreateMachineGUID()
{
try
{
// Check for and create the MachineGUID registry key used to uniquely identify this machine
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd") == null)
Microsoft.Win32.Registry.LocalMachine.CreateSubKey("SOFTWARE\\EigoLtd");
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd\\Cryptography") == null)
Microsoft.Win32.Registry.LocalMachine.CreateSubKey("SOFTWARE\\EigoLtd\\Cryptography");
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd\\Cryptography").GetValue("MachineGuid") == null)
{
Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd\\Cryptography", true).SetValue("MachineGuid", mGetNewGUID(), Microsoft.Win32.RegistryValueKind.String);
}
return true;
}
catch (Exception ex)
{
mHandleException(ex, "mCreateMachineGUID");
return false;
}
}
This UUID can then be read from the registry by any application by using code like the following.
Guid guidMachineGUID;
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd\\Cryptography") != null)
{
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd\\Cryptography").GetValue("MachineGuid") != null)
{
// Get the stored value
guidMachineGUID = new Guid(Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\EigoLtd\\Cryptography").GetValue("MachineGuid").ToString());
}
}