How to import an externally-generated AES key into a TPM2 using TSS.NET

Ballentine, Casey 0 Reputation points
2025-07-31T23:42:03.2433333+00:00

Using TSS.NET on Windows 11 with .Net Core 8.0, I've been trying to import an externally-generated (via System.Security.Cryptography) AES key into the TPM on my system. Devices in this system will use this same TPM-protected AES key to encrypt & decrypt data shared between them.

In the below code, the call to _tpm2.Import() is always failing and I can't figure out why. It fails whether using a simulator via TcpTpmDevice or when using my device's TPM via TbsDevice.

Here's the code that's failing:

		/// <summary>
		/// Stores a raw AES key (byte array) into the TPM as a persistent symmetric key.
		/// Returns the persistent handle of the stored key.
		/// </summary>
		/// <param name="keyBytes">The raw AES key bytes (must match expected key size, e.g., 32 bytes for 256-bit AES)</param>
		/// <returns>The persistent TpmHandle of the stored key</returns>
		public TpmHandle StoreSymmetricKeyToTpm(byte[] keyBytes)
		{
			// Define the symmetric key parameters
			SymDefObject symDef = new SymDefObject(TpmAlgId.Aes, defaultSymKeyBits, defaultSymKeyMode);
			// Create a SymCipher object from the raw key bytes
			SymCipher symKey = SymCipher.Create(symDef, keyBytes);
			// Create a public area for the AES key
			TpmPublic importKeyTemplate = new TpmPublic(
				defaultHashAlg,
			    ObjectAttr.Decrypt | ObjectAttr.Encrypt | ObjectAttr.FixedTPM | ObjectAttr.FixedParent | ObjectAttr.UserWithAuth,
				null,
				new SymDefObject(symDef),
				new Tpm2bDigestSymcipher()
			);
			// Wrap the externally created AES key for import
			TssObject aesKeyWrapper = TssObject.Create(importKeyTemplate, new AuthValue(defaultUserAuthChain));
			byte[] encSecret;
			TpmPublic tempParentPubKey = _tpm2.ReadPublic(_primaryHandle, out _, out _);
			TpmPrivate duplicationBlob = aesKeyWrapper.GetDuplicationBlob(tempParentPubKey, symKey, out encSecret);
			// Import the wrapped key into the TPM
			TpmPrivate importedPvt = _tpm2.Import(_primaryHandle, symKey, aesKeyWrapper.Public, duplicationBlob, encSecret, new SymDefObject(symDef));
			// Load the imported key into the TPM
			TpmHandle loadedKeyHandle = _tpm2.Load(_primaryHandle, importedPvt, aesKeyWrapper.Public)
				.SetAuth(new AuthValue(defaultUserAuthChain));
			// Make the key persistent
			TpmHandle persistentHandle = new TpmHandle((uint)TpmHc.PersistentFirst + 0x20); // Use a unique persistent handle
			_tpm2._ExpectResponses(TpmRc.Success, TpmRc.NvSpace, TpmRc.NvDefined)
				.EvictControl(TpmRh.Owner, loadedKeyHandle, persistentHandle);
			// Optionally flush the transient handle
			_tpm2.FlushContext(loadedKeyHandle);
			_aesKeyHandle = persistentHandle;
			return persistentHandle;
		}

I know that _primaryHandle is a valid Primary Storage Key because I can create RSA key pairs and persist/retrieve them from the TPM. Also, defaultSymKeyBits is 256 and defaultSymKeyMode is TpmAlgId.Cfb

The call to Tpm2.Import() is always throwing an exception with the following Message text:

Error {Attributes} was returned for command Import.
Details:
[Code=TpmRc.Attributes],[RawCode=0x2C2,706]
[ErrorEntity=Parameter], [ParmNum=2]
[ParmName=objectPublic]

Developer technologies | C#
0 comments No comments
{count} votes

Accepted answer
  1. Gade Harika (INFOSYS LIMITED) 250 Reputation points Microsoft External Staff
    2025-08-05T05:38:43.1066667+00:00

    Thanks for sharing your code and error details! Here's a step-by-step guide to help you fix the issue with importing an externally-generated AES key into TPM using TSS.NET:

    Step-by-Step Fix

    Understand the Error The TPM is rejecting your Import() call because the object attributes in TpmPublic are not valid for importing a symmetric key. The error says {Attributes} is the problem.

    1. Update Object Attributes You're currently using
      ObjectAttr.Decrypt | ObjectAttr.Encrypt | ObjectAttr.FixedTPM | ObjectAttr.FixedParent | ObjectAttr.UserWithAuth Instead, use: csharp
         ObjectAttr.Decrypt | ObjectAttr.Encrypt | ObjectAttr.UserWithAuth
      
      This removes attributes that are only valid for TPM-generated keys (FixedTPM, FixedParent, SensitiveDataOrigin).
      3.Check Name Algorithm If you're not using a policy, you can set the name algorithm to TpmAlgId.Null: csharp
         TpmAlgId.Null
      
      Otherwise, TpmAlgId.Sha256 is fine. 4.Update Your TpmPublic Definition Here's the corrected version:
      csharp
         TpmPublic importKeyTemplate = new TpmPublic(
         TpmAlgId.Sha256, // or TpmAlgId.Null
         ObjectAttr.Decrypt | ObjectAttr.Encrypt | ObjectAttr.UserWithAuth,
         null,
         new SymDefObject(symDef),
         new Tpm2bDigestSymcipher()
      
         
         **5.Try Again** With the updated attributes, rerun your code. The `Import()` call should now succeed.  
         
         
         Here's your updated `StoreSymmetricKeyToTpm` method with the necessary changes applied to fix the import issue:  
         **Updated Method:** `StoreSymmetricKeyToTpm`  
         
         `public TpmHandle StoreSymmetricKeyToTpm(byte[] keyBytes)`
         
         `{`
         
         `    // Define the symmetric key parameters`
         
         `    SymDefObject symDef = new SymDefObject(TpmAlgId.Aes, defaultSymKeyBits, defaultSymKeyMode);`
         
         `    // Create a SymCipher object from the raw key bytes`
         
         `    SymCipher symKey = SymCipher.Create(symDef, keyBytes);`
         
         `    // Updated object attributes for importing a symmetric key`
         
         `    ObjectAttr attrs = ObjectAttr.Decrypt | ObjectAttr.Encrypt | ObjectAttr.UserWithAuth;`
         
         `    //  Use a valid name algorithm (Sha256 or Null if no policy is used)`
         
         `    TpmAlgId nameAlg = TpmAlgId.Sha256;`
         
         `    // Create a public area for the AES key`
         
         `    TpmPublic importKeyTemplate = new TpmPublic(`
         
         `        nameAlg,`
         
         `        attrs,`
         
         `        null,`
         
         `        new SymDefObject(symDef),`
         
         `        new Tpm2bDigestSymcipher()`
         
         `    );`
         
         `    // Wrap the externally created AES key for import`
         
         `    TssObject aesKeyWrapper = TssObject.Create(importKeyTemplate, new AuthValue(defaultUserAuthChain));`
         
         `    // Get the public part of the parent key`
         
         `    TpmPublic tempParentPubKey = _tpm2.ReadPublic(_primaryHandle, out _, out _);`
         
         `    // Create duplication blob and encrypted seed`
         
         `    byte[] encSecret;`
         
         `    TpmPrivate duplicationBlob = aesKeyWrapper.GetDuplicationBlob(tempParentPubKey, symKey, out encSecret);`
         
         `    // Import the wrapped key into the TPM`
         
         `    TpmPrivate importedPvt = _tpm2.Import(_primaryHandle, symKey, aesKeyWrapper.Public, duplicationBlob, encSecret, new SymDefObject(symDef));`
         
         `    // Load the imported key into the TPM`
         
         `    TpmHandle loadedKeyHandle = _tpm2.Load(_primaryHandle, importedPvt, aesKeyWrapper.Public)`
         
         `        .SetAuth(new AuthValue(defaultUserAuthChain));`
         
         `    // Make the key persistent`
         
         `    TpmHandle persistentHandle = new TpmHandle((uint)TpmHc.PersistentFirst + 0x20); // Use a unique persistent handle`
         
         `    _tpm2._ExpectResponses(TpmRc.Success, TpmRc.NvSpace, TpmRc.NvDefined)`
         
         `        .EvictControl(TpmRh.Owner, loadedKeyHandle, persistentHandle);`
         
         `    // Optionally flush the transient handle`
         
         `    _tpm2.FlushContext(loadedKeyHandle);`
         
         `    // Save and return the persistent handle`
         
         `    _aesKeyHandle = persistentHandle;`
         
         `    return persistentHandle;`
         
         `}`
         
      
      Let us know if the issue persists after following these steps. We’ll be happy to assist further if needed. If the issue has been resolved, kindly mark the response as answered."
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.