The idea I am toying with is using a few serial numbers or unique id's related to the hardware and hashing them together.
Things that get upgraded:
-Memory
-MACs (can be spoofed, usb adapters get plugged in, etc.)
Things that don't get upgraded often:
-CPU
-BIOS
-Motherboard
Using WMIC can be a great way to grab some info, I would start by grabbing things that don't change often as the first and preferred choice, I would like to be able to fingerprint at least 2 serial numbers or devices to use for generating a registration key.
wmic cpu get DeviceId /format:value
That will grab the CPU ID, you could run that command for:
1 - CPU (cpu:DeviceID)
2 - Motherboard (baseboard:serialnumber)
3 - BIOS (bios:serialnumber)
if you don't get at least 2 populated values, then grab
4 - Network Adapter - (nic:MACAddress)
5 - RAM - (memphysical:SerialNumber)
Depending on your business logic you can use the first two serial numbers available to create your registration number, and if you always follow the same order then on re-installs the registration number will still work, however if a device changes or a user tries to install on a secondary computer the id's change invalidating the registration number. To reduce the amount of tech support calls the least amount of hardware you fingerprint will give the least amount of headaches and if you try to fingerprint the least likely items to be upgraded that further reduces headaches. My preference is the order above.
You could use a Diffie-Hellman key exchange scheme to have the user generate a private/pulic key pair with their hardware id's as a payload, then pass this information up to a registration server where the registration server would use a public/private key to decrypt the payload and compute the registration key to return back to the end user. I like to use JWT to pass things back and forth witht he public keys included in the payload of the JWT. Hope that helps.
UUID was mentioned above and is a great idea you can get that by using the below command from your windows cmd.exe:
wmic csproduct get UUID /format:value
Disclaimer these command only work for Windows I think 2000 and above but you would need to verify, they maybe available for systems below 2000 but at that point I really try not to support those devices. Good luck. *Looks like WMI is being deprecated in favor of powershell so to keep this post current here are the power shell commands.
Get-CimInstance -ClassName Win32_Processor | Select SerialNumber
Get-CimInstance -ClassName Win32_BaseBoard | Select SerialNumber
Get-CimInstance -ClassName Win32_Bios | Select SerialNumber
Get-NetAdapter -Physical | Where-Object Status -like Up | Select-Object MacAddress
Get CimInstance -ClassName Win32_PhysicalMemory | Select SerialNumber
The network adapter cmdlet will only check for physical adapters so a virtual adapter couldn't be used and manipulated and I like to use the first adapter that is Up or being used so that a spare NIC can't be swapped around for install reasons.
On Mac:
system_profiler | grep "Serial Number (system)"
On Linux (debian):
sudo dmidecode -t system | grep "Serial Number"
dmidecode and system_profiler has other components it can grab serial numbers from similar to wmic in windows. I don't work on macs so I can't confirm a list of exact specs but creating a list of LCD (least common denominator) the serial numbers for the parts that all three commands can access is put together and groomed to the least likely parts to be upgraded or changed. Then a combination of the top 2-3 numbers hashed can make for a unique machine id that's a bit more robust and allows a cross platform app to be activated even on a device with it's operating system updated.