Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

TPM identification sample code

All TPM chips contain an Endorsement Key (EK) that is unique for every TPM and can identify it. The EK can't be changed or removed.

The public-key part of the TPM Endorsement Key (EKpub) can thererfore serve as a unique and tamper-proof machine identifier. A more compact secure identifier can be computed by taking the SHA-256 hash of EKpub.

PowerShell sample

Retrieve EKpub from TPM:

(Get-TpmEndorsementKeyInfo -Hash "Sha256").PublicKeyHash

This command unfortunately require admin privileges. This is strange, since the underlying Win32 APIs does not require admin privileges.

C++ sample

The C++ TpmIdentifier project in this folder demonstrates how to retrieve EKpub from TPM and compute the SHA-256 hash without admin privileges. It also demonstrates how to compute the CRC-32 checksum if a more compact 32bit machine identifier is needed.

C# sample

Retrieve EKpub from TPM without admin privileges:

// Add System.IO.Hashing & Microsoft.Windows.CsWin32 NuGet packages
// Add NativeMethods.txt to project folder with NCryptOpenStorageProvider, NCryptGetProperty, NCRYPT_PCP_RSA_EKPUB_PROPERTY & BCRYPT_RSAKEY_BLOB lines to enable PInvoke calls
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Windows.Win32;
using Windows.Win32.Security.Cryptography;

byte[] EKblob; // RSA public key blob in BCRYPT_RSAKEY_BLOB format
{
    // Connect to TPM chip
    NCryptFreeObjectSafeHandle handle;
    PInvoke.NCryptOpenStorageProvider(out handle, CngProvider.MicrosoftPlatformCryptoProvider.Provider, 0);

    // Get EKpub blob
    EKblob = new byte[1024];
    uint blobLen = 0;
    PInvoke.NCryptGetProperty(handle, PInvoke.NCRYPT_PCP_RSA_EKPUB_PROPERTY, EKblob, out blobLen, 0);
    Array.Resize(ref EKblob, (int)blobLen);

    handle.Close();
}

byte[] EKpub; // ASN.1 DER encoding of the RSA public key
{
    // Extract RSA modulus and exponent
    var tmp = GCHandle.Alloc(EKblob, GCHandleType.Pinned);
    var header = (BCRYPT_RSAKEY_BLOB)Marshal.PtrToStructure(tmp.AddrOfPinnedObject(), typeof(BCRYPT_RSAKEY_BLOB))!;
    tmp.Free();

    // Exponent & Modulus follows the header in the blob (https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_rsakey_blob)
    var rsa = new RSAParameters();
    rsa.Exponent = new byte[header.cbPublicExp];
    Array.Copy(EKblob, Marshal.SizeOf(header), rsa.Exponent, 0, header.cbPublicExp);
    rsa.Modulus = new byte[header.cbModulus];
    Array.Copy(EKblob, Marshal.SizeOf(header) + header.cbPublicExp, rsa.Modulus, 0, header.cbModulus);

    EKpub = RSA.Create(rsa).ExportRSAPublicKey();
}

// Compute EKpub hash & checksum
var sha256 = SHA256.HashData(EKpub);
Console.WriteLine("SHA-256 of EKpub: " + BitConverter.ToString(sha256));
var crc32 = System.IO.Hashing.Crc32.Hash(EKpub);
Array.Reverse(crc32); // to big endian
Console.WriteLine("CRC-32 of EKpub: " + BitConverter.ToString(crc32));