使用Microsoft.TSS向TPM存储超1024字节数据失败,求排查建议
问题描述
我正在开发一款面向Windows和Red Hat Linux虚拟机的.NET应用,用于向TPM存储中读写字符串,使用的是Microsoft.TSS(2.1.1)NuGet包。目前仅能存储约1024字节的byte[]数据,尝试存储约4000字节(我的最大数据尺寸)时操作失败。以下是存储和读取数据的代码,恳请提供建议。
Store() 方法
private ushort defaultDataSize = 5120; private AuthValue nvAuthValue = new AuthValue(new byte[] { 1, 2, 3, 4, 5 }); Tpm2Device tpmDevice = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new TbsDevice() : new LinuxTpmDevice(); tpmDevice.Connect(); var tpm = new Tpm2(tpmDevice); var ownerAuth = new AuthValue(); var nvHandle = TpmHandle.NV(int.Parse(key)); tpm[ownerAuth]._AllowErrors().NvUndefineSpace(TpmHandle.RhOwner, nvHandle); tpm[ownerAuth].NvDefineSpace(TpmHandle.RhOwner, nvAuthValue, new NvPublic(nvHandle, TpmAlgId.Sha1, NvAttr.Authwrite | NvAttr.Authread, new byte[0], defaultDataSize)); tpm[nvAuthValue].NvWrite(nvHandle, nvHandle, Encoding.ASCII.GetBytes(value ?? string.Empty), 0); tpm.Dispose();
Get() 方法
private ushort defaultDataSize = 5120; private AuthValue nvAuthValue = new AuthValue(new byte[] { 1, 2, 3, 4, 5 }); Tpm2Device tpmDevice = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new TbsDevice() : new LinuxTpmDevice(); tpmDevice.Connect(); var tpm = new Tpm2(tpmDevice); var nvHandle = TpmHandle.NV(int.Parse(key)); byte[] storedBytes = tpm[nvAuthValue].NvRead(nvHandle, nvHandle, defaultDataSize, 0); return storedBytes == null ? string.Empty : Encoding.ASCII.GetString(storedBytes).Trim('\0'); tpm.Dispose();
问题分析与解决方案
1. 核心问题:TPM单次写入长度限制
TPM的NvWrite命令受限于传输缓冲区大小,单次写入的最大数据量通常为1024字节,直接写入4000字节会超出限制导致失败。需要拆分数据分块写入。
2. 修复后的Store()方法
将大字节数组拆分为1024字节的块,逐块写入并更新偏移量:
private ushort defaultDataSize = 5120; private AuthValue nvAuthValue = new AuthValue(new byte[] { 1, 2, 3, 4, 5 }); Tpm2Device tpmDevice = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new TbsDevice() : new LinuxTpmDevice(); tpmDevice.Connect(); var tpm = new Tpm2(tpmDevice); var ownerAuth = new AuthValue(); var nvHandle = TpmHandle.NV(int.Parse(key)); // 清理已有NV空间 tpm[ownerAuth]._AllowErrors().NvUndefineSpace(TpmHandle.RhOwner, nvHandle); // 定义目标NV空间 tpm[ownerAuth].NvDefineSpace(TpmHandle.RhOwner, nvAuthValue, new NvPublic(nvHandle, TpmAlgId.Sha1, NvAttr.Authwrite | NvAttr.Authread, new byte[0], defaultDataSize)); byte[] data = Encoding.ASCII.GetBytes(value ?? string.Empty); int offset = 0; const int chunkSize = 1024; // TPM单次写入的典型上限 while (offset < data.Length) { int currentChunkLength = Math.Min(chunkSize, data.Length - offset); byte[] chunk = new byte[currentChunkLength]; Array.Copy(data, offset, chunk, 0, currentChunkLength); tpm[nvAuthValue].NvWrite(nvHandle, nvHandle, chunk, (uint)offset); offset += currentChunkLength; } tpm.Dispose();
3. 读取逻辑适配(可选优化)
为保证兼容性,读取时也可采用分块方式:
private ushort defaultDataSize = 5120; private AuthValue nvAuthValue = new AuthValue(new byte[] { 1, 2, 3, 4, 5 }); Tpm2Device tpmDevice = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new TbsDevice() : new LinuxTpmDevice(); tpmDevice.Connect(); var tpm = new Tpm2(tpmDevice); var nvHandle = TpmHandle.NV(int.Parse(key)); List<byte> storedBytesList = new List<byte>(); int offset = 0; const int chunkSize = 1024; while (offset < defaultDataSize) { int currentReadLength = Math.Min(chunkSize, defaultDataSize - offset); byte[] chunk = tpm[nvAuthValue].NvRead(nvHandle, nvHandle, (ushort)currentReadLength, (uint)offset); if (chunk == null || chunk.Length == 0) break; storedBytesList.AddRange(chunk); offset += chunk.Length; } byte[] storedBytes = storedBytesList.ToArray(); tpm.Dispose(); return storedBytes == null ? string.Empty : Encoding.ASCII.GetString(storedBytes).Trim('\0');
4. 额外注意事项
- 确认虚拟机中TPM配置支持的NV空间最大尺寸,避免定义超出上限的空间
- 添加错误捕获逻辑,检查
NvDefineSpace、NvWrite等操作的返回状态 - 若使用非ASCII编码,需重新计算字节长度,防止数据截断
内容的提问来源于stack exchange,提问作者dsaiju
相关产品推荐
相关产品推荐

