当前位置: 首页 > 知识库问答 >
问题:

如何使用TPM(Trusted Platform Module)加密字节

吕自怡
2023-03-14

如何使用计算机的TPM模块加密字节?

Windows提供了一个(相对)简单的API来使用CryptProtectData对blob进行加密,我们可以包装一个易于使用的函数:

public Byte[] ProtectBytes(Byte[] plaintext)
{
   //...
}

ProtectBytes的细节不如您可以轻松使用它的想法重要:

  • 以下是我想要用系统中的密钥加密的字节
  • 把加密的blob还给我

返回的blob是一个未记录的留档结构,包含解密和返回原始数据所需的一切(哈希算法、密码算法、盐、HMAC签名等)。

为完整起见,以下是ProtectBytes的示例伪代码实现,它使用Crypt API来保护字节:

public Byte[] ProtectBytes(Byte[] plaintext)
{
   //Setup our n-byte plaintext blob
   DATA_BLOB dataIn;
   dataIn.cbData = plaintext.Length;
   dataIn.pbData = Addr(plaintext[0]);

   DATA_BLOB dataOut;

   //dataOut = EncryptedFormOf(dataIn)
   BOOL bRes = CryptProtectData(
         dataIn,
         null,     //data description (optional PWideChar)
         null,     //optional entropy (PDATA_BLOB)
         null,     //reserved
         null,     //prompt struct
         CRYPTPROTECT_UI_FORBIDDEN || CRYPTPROTECT_LOCAL_MACHINE,
         ref dataOut);
   if (!bRes) then
   {
      DWORD le = GetLastError();
      throw new Win32Error(le, "Error calling CryptProtectData");
   }

   //Copy ciphertext from dataOut blob into an actual array
   bytes[] result;
   SetLength(result, dataOut.cbData);
   CopyMemory(dataOut.pbData, Addr(result[0]), dataOut.cbData);

   //When you have finished using the DATA_BLOB structure, free its pbData member by calling the LocalFree function
   LocalFree(HANDLE(dataOut.pbData)); //LocalFree takes a handle, not a pointer. But that's what the SDK says.
}

以上代码仅用于加密本地计算机的数据。使用系统帐户作为密钥生成器对数据进行加密(详细信息虽然有趣,但并不重要)。最终的结果是,我可以加密只能由本地计算机解密的数据(例如硬盘加密主密钥)。

现在是时候让这更进一步了。我想加密一些只能由本地TPM解密的数据(例如硬盘加密主密钥)。换言之,我想用Windows中的TPM替换下面框图中针对Android的高通可信执行环境(TEE):

注意:我意识到TPM不做数据签名(或者如果做了,它不保证每次对相同的数据签名都会给出相同的二进制输出)。这就是为什么我愿意将“RSA签名”替换为“使用硬件绑定密钥加密256位blob”。

问题是TPM编程在MSDN上完全没有记录。没有可用于执行任何操作的API。相反,您必须为自己找到可信计算组软件堆栈(又名TSS)的副本,找出要发送到TPM的命令、有效负载、顺序,并调用Windows的Tbsip_Submit_Command函数直接提交命令:

TBS_RESULT Tbsip_Submit_Command(
  _In_     TBS_HCONTEXT hContext,
  _In_     TBS_COMMAND_LOCALITY Locality,
  _In_     TBS_COMMAND_PRIORITY Priority,
  _In_     const PCBYTE *pabCommand,
  _In_     UINT32 cbCommand,
  _Out_    PBYTE *pabResult,
  _Inout_  UINT32 *pcbOutput
);

Windows没有更高级别的API来执行操作。

这在道义上相当于试图通过向硬盘发出SATA I/O命令来创建文本文件。

可信计算组(TCG)确实定义了他们自己的API:TCB软件堆栈(TSS)。这个API的实现是由一些人创建的,称为TrouSerS。然后一个人将该项目移植到Windows。

该代码的问题在于它无法移植到Windows世界。例如,您不能从Delphi使用它,也不能从C#使用它。它要求:

  • OpenSSL

我只想让代码用我的TPM加密一些东西。

上述CryptProtectData只需要函数体中的内容。

使用TPM加密数据的等效代码是什么?正如其他人所指出的,您可能必须参考三个TPM手册,并自己构建BLOB。它可能涉及TPM\U seal命令。虽然我不想密封数据,但我想我想绑定它:

绑定–使用TPM bind key(从存储密钥派生的唯一RSA密钥)加密数据。密封–以与绑定类似的方式对数据进行加密,但还指定TPM必须处于的状态,以便对数据进行解密(未密封)

为了找到我需要的20行代码,我试着阅读三个必需的卷:

  • 第1部分-设计原则
  • 第2部分-TPM的结构
  • 第3部分-命令

但我不知道我在读什么。如果有任何类型的教程或示例,我可能会尝试一下。但我完全迷路了。

以同样的方式,我能够提供:

Byte[] ProtectBytes_Crypt(Byte[] plaintext)
{
   //...
   CryptProtectData(...); 
   //...
}

是否有人可以提供相应的等价物:

Byte[] ProtectBytes_TPM(Byte[] plaintext)
{
   //...
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   //...snip...
   Tbsip_Submit_Command(...);
   //...
}

除了TPM中锁定了钥匙而不是LSA中锁定的钥匙外,这也会发生同样的事情吗?

我不知道bind到底是什么意思。但在查看TPM Main-第3部分命令-规范版本1.2时,会提到bind:

10.3 TPM\U解除绑定

TPM\U UnBind获取Tspi\U data\U Bind命令生成的数据blob,并对其解密以导出给用户。调用方必须授权使用将解密传入blob的密钥。TPM\U UnBind以块为基础进行操作,不知道一个块和另一个块之间的任何关系。

令人困惑的是,没有Tspi\U Data\U Bind命令。

令人恐怖的是,从来没有人费心记录TPM或其操作。这就好像他们把所有的时间都花在了这个很酷的东西上,但是不想处理让它可以用于某些东西的痛苦步骤。

从(现在)免费书籍《TPM 2.0实用指南:在新的安全时代使用Trusted Platform模块》开始:

TPM可以访问自己生成的私钥,因此它可以使用公钥加密密钥,然后将生成的blob存储在硬盘上。这样,TPM可以保留几乎无限数量的密钥可供使用,但不会浪费宝贵的内部存储空间。存储在硬盘上的密钥可以被擦除,但也可以备份,这在设计人员看来是一种可以接受的权衡。

如何使用TPM的公钥加密密钥?

在过去几年中,基于web的应用程序的数量有所增加。其中包括基于web的备份和存储。现在有许多公司提供此类服务,但据我们所知,这些服务的任何客户端都不允许用户将备份服务的密钥锁定到TPM。如果这样做了,那么如果通过在多台机器上复制TPM密钥来备份TPM密钥本身,那当然很好。这似乎是开发人员的一个机会。

开发人员如何锁定TPM的密钥?

典型的密码文件存储密码的salted散列。验证包括对提供的密码进行盐析和散列,并将其与存储的值进行比较。由于计算不包含秘密,因此密码文件会受到脱机攻击。

此用例使用TPM生成的HMAC密钥。密码文件存储加盐密码的HMAC。验证包括加盐和HMACing提供的密码并将其与存储的值进行比较。由于离线攻击者没有HMAC密钥,因此攻击者无法通过执行计算来发起攻击。

这可能有用。如果TPM有一个秘密HMAC密钥,并且只有我的TPM知道HMAC密钥,那么我可以将“Sign(aka TPM encrypt with it’s private key)”替换为“HMAC”。但在接下来的一行中,他完全颠倒了自己:

TPM2_Create,指定HMAC密钥

如果必须指定HMAC密钥,则这不是TPM机密。当您意识到这是关于TPM提供的加密实用程序的一章时,HMAC密钥不是机密的事实是有意义的。您不必自己编写SHA2、AES、HMAC或RSA,您可以重用TPM已有的功能。

作为一种安全设备,应用程序在硬件设备中使用密钥的同时保持密钥安全的能力是TPM最大的优势。TPM可以生成和导入外部生成的密钥。它支持非对称密钥和对称密钥。

杰出的你是怎么做到的!?

可以说,TPM最大的优势在于它能够生成加密密钥并在硬件边界内保护其机密。密钥生成器基于TPM自己的随机数生成器,不依赖外部随机性源。因此,它消除了基于熵源不足的弱软件的弱点。

TPM是否有能力在硬件边界内生成加密密钥并保护其机密?是这样吗,怎么做?

如果TPM保护加密密钥,则全磁盘加密应用程序要比存储在同一磁盘上(仅受密码保护)安全得多。首先,TPM硬件具有防锤击保护(有关TPM字典攻击保护的详细说明,请参阅第8章),这使得对密码进行暴力攻击不切实际。只有软件保护的密钥更容易受到弱密码的攻击。其次,存储在磁盘上的软件密钥更容易被窃取。拿着磁盘(或磁盘的备份),你就得到了钥匙。当TPM持有密钥时,整个平台,或者至少磁盘和主板,都必须被盗。

密封允许密钥不仅受到密码的保护,还受到策略的保护。典型的策略将密钥锁定为密封时当前的PCR值(软件状态)。这假设第一次启动时的状态没有受到损害。第一次启动时存在的任何预安装的恶意软件都将被测量到PCR中,因此密钥将被密封到受损害的软件状态。不太信任的企业可能拥有标准磁盘映像并密封到代表该映像的PCR。这些PCR值将在可能更受信任的平台上预先计算。更复杂的企业会使用TPM2_PolicyAuthorize,并提供几张授权一组受信任的PCR值的票证。有关策略授权及其在解决PCR脆性问题中的应用的详细描述,请参阅第14章。

虽然密码也可以保护密钥,但即使没有TPM密钥密码,也可以获得安全性。攻击者可以在不提供TPMkey密码的情况下启动平台,但在没有操作系统用户名和密码的情况下无法登录。骨安全保护数据。攻击者可以启动其他操作系统,例如从实时DVD或U盘启动,而不是从硬盘启动,以绕过操作系统登录安全性。然而,这种不同的引导配置和软件会改变PCR值。由于这些新的PCR与密封的值不匹配,TPM不会释放解密密钥,硬盘驱动器也无法解密。

太棒了!这正是我碰巧想要的用例。这也是Microsoft使用TPM的用例。我该怎么做!?

所以我读了整本书,它没有提供任何有用的东西。这非常令人印象深刻,因为它有375页。你想知道这本书里有什么内容——回头看,我不知道。

因此,我们放弃了TPM编程的最终指南,转而使用Microsoft提供的一些文档:

来自Microsoft TPM Platform Crypto Provider Toolkit。它确切地提到了我想做什么:

EK旨在为平台提供可靠的加密标识符。企业可能会维护属于其企业中所有PC的TPM的认可密钥数据库,或者数据中心结构控制器可能会在所有刀片中拥有TPM数据库。在Windows上,您可以使用“Windows 8中的平台加密提供程序”一节中描述的NCrypt提供程序来读取EK的公共部分。

TPM中的某个地方有一个RSA私钥。那把钥匙锁在那里,永远不会被外界看到。我希望TPM使用其私钥对某些内容进行签名(即使用其私钥对其进行加密)。

所以我想要可能存在的最基本的操作:

用你的私钥加密一些东西。我甚至还没有要求更复杂的东西:

  • “密封”基于PCR状态
  • 创建密钥并将其存储在易失性或非易失性内存中
  • 创建对称密钥并尝试将其加载到TPM

我要求TPM可以执行的最基本操作。为什么不可能得到关于如何做的任何信息?

当我说RSA签名是TPM可以做的最基本的事情时,我想我是在油嘴滑舌。TPM可以要求做的最基本的事情是给我随机字节。我已经想出了怎么做:

public Byte[] GetRandomBytesTPM(int desiredBytes)
{
   //The maximum random number size is limited to 4,096 bytes per call
   Byte[] result = new Byte[desiredBytes];

   BCRYPT_ALG_HANDLE hAlgorithm;

   BCryptOpenAlgorithmProvider(
         out hAlgorithm,
         BCRYPT_RNG_ALGORITHM, //AlgorithmID: "RNG"
         MS_PLATFORM_CRYPTO_PROVIDER, //Implementation: "Microsoft Platform Crypto Provider" i.e. the TPM
         0 //Flags
   );
   try
   {                
      BCryptGenRandom(hAlgorithm, @result[0], desiredBytes, 0);
   }
   finally
   {
      BCryptCloseAlgorithmProvider(hAlgorithm);
   }

   return result;
}

我意识到使用TPM的人数非常少。这就是为什么Stackoverflow上没有人有答案。所以我真的不能太贪心,想办法解决我的共同问题。但我真正想做的是“密封”一些数据:

  • 向TPM呈现一些数据(例如32字节的关键材料)
  • 让TPM加密数据,返回一些不透明的blob结构
  • 稍后要求TPM解密blob
  • 只有当TPM的PCR寄存器与加密期间相同时,解密才会起作用。

换句话说:

Byte[] ProtectBytes_TPM(Byte[] plaintext, Boolean sealToPcr)
{
   //...
}

Byte[] UnprotectBytes_TPM(Byte[] protectedBlob)
{
   //...
}

Windows中最初的加密API被称为Crypto API。

从Windows Vista开始,加密API已被加密API:下一代(内部称为BestCrypt,缩写为BCrypt,不要与密码哈希算法混淆)所取代。

Windows附带两个BCrypt提供程序:

  • Microsoft Primitive Provider(MS\u Primitive\u Provider)默认:所有原语的默认软件实现(哈希、对称加密、数字签名等)

MSDN中未记录平台加密提供程序,但有来自2012年Microsoft研究网站的文档:

TPM Platform Crypto Provider and Toolkit包含在Windows 8中使用TPM相关功能的示例代码、实用程序和文档。所描述的子系统包括TPM支持的加密下一代(CNG)平台加密提供程序,以及认证服务提供程序如何使用新的Windows功能。支持基于TPM1.2和TPM2.0的系统。

似乎微软的意图是向加密NG API的微软平台加密提供商展示TPM加密功能。

鉴于:

  • 我想执行RSA非对称加密(使用TPM)

一个前进的方向可能是找出如何使用Microsoft Cryptography Next Gen API进行数字签名。

我的下一步将是使用标准提供程序(MS\u PRIMITIVE\u provider)使用RSA公钥在BCrypt中编写加密代码。例如。:

  • 模数:0xDC 67 FA F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94 27 50 8209 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1 69 FB 995D 85 0C 07 A1 F9 47 1B 56 16 E F6 7F B9 CF 2A 58 36 37 99 29 AA 4F A8 E8 4F C7 82 2B 9D 72 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4 DD 7C 5C 1A C8 51 A9 AC DF 08 22 04 D7 4B 09 DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB 37 9B 3F D3 74 65 24 0D FF 34 75 57A4 F5 BF 55
  • 公共指数(publicExponent):65537

有了该代码,我可能可以切换到使用TPM提供程序(MS\u PLATFORM\u CRYPTO\u提供程序)。

2016年2月22日:随着Apple被迫帮助解密用户数据,人们对如何让TPM执行它发明的最简单的任务——加密某些东西重新产生了兴趣。

这大致相当于每个人都有一辆车,但没有人知道如何启动一辆车。如果我们能够通过步骤1,它可以做非常有用和酷的事情。

Microsoft的TPM Base Services存档留档主页表示,我们可能希望改用Key Storage API:

注意事项

TPM可用于关键存储操作。但是,鼓励开发人员在这些场景中使用关键的存储API。密钥存储API提供了创建、签名或加密以及保存加密密钥的功能,对于这些目标场景,它们比TBS级别更高,更易于使用。

密钥存储API存档的介绍说明:

CNG提供了一种私钥存储模型,允许适应当前和未来创建使用加密功能(如公钥或私钥加密)的应用程序的需求,以及密钥材料的存储需求。密钥存储路由器是该模型中的中心例程,用Ncrypt实现。dll。应用程序通过密钥存储路由器访问系统上的密钥存储提供程序(KSP),该路由器对应用程序和存储提供程序本身隐藏了密钥隔离等细节。下图显示了CNG密钥隔离体系结构的设计和功能。

他们还指出,支持硬件安全模块(可能是TPM的术语):

如上所述,可以支持范围广泛的硬件存储设备。在每种情况下,所有这些存储设备的接口都是相同的。它包括执行各种私钥操作的功能以及与密钥存储和管理相关的功能。

我唯一不知道的是,您是否必须要求使用HSM,或者它是否在可用时自动发生(以及如何知道何时不可用,所以您无论如何都不会尝试继续)。

  • Android-加密-存储加密密钥
  • Android Explorations-重新审视Android磁盘加密
  • DPAPI秘密。DPAPI中的安全分析和数据恢复(第1部分)
  • 加密下一代:在TPM中存储密钥
  • 如何通过CNG KSP API从TPM导出RSA私钥

共有3个答案

董和泽
2023-03-14

可信密钥和加密密钥是添加到现有内核密钥环服务中的两种新密钥类型。这两种新类型都是可变长度的对称密钥,在这两种情况下,所有密钥都是在内核中创建的,用户空间只能看到、存储和加载加密的blob。可信密钥要求提供可信平台模块(TPM)芯片以提高安全性,而加密密钥可以在任何系统上使用。为方便起见,所有用户级Blob均以十六进制ascii显示和加载,并经过完整性验证。

可信密钥使用TPM生成和密封密钥。密钥在TPM中的2048位RSA密钥下密封,并且可以选择密封到指定的PCR(完整性测量)值,并且只有在PCR和blob完整性验证匹配的情况下才由TPM解锁。加载的可信密钥可以用新的(未来的)PCR值更新,因此密钥很容易迁移到新的PCR值,例如在更新内核和initramfs时。同一个键可以在不同的PCR值下保存多个blob,因此很容易支持多个引导。

默认情况下,受信任密钥密封在SRK下,SRK具有默认授权值(20个零)。这可以在takeownership时使用trouser的实用程序设置:tpm\u takeownership-u-z

Usage:
    keyctl add trusted name "new keylen [options]" ring
    keyctl add trusted name "load hex_blob [pcrlock=pcrnum]" ring
    keyctl update key "update [options]"
    keyctl print keyid

    options:
    keyhandle= ascii hex value of sealing key default 0x40000000 (SRK)
    keyauth=   ascii hex auth for sealing key default 0x00...i
        (40 ascii zeros)
    blobauth=  ascii hex auth for sealed data default 0x00...
        (40 ascii zeros)
    blobauth=  ascii hex auth for sealed data default 0x00...
        (40 ascii zeros)
    pcrinfo=   ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
    pcrlock=   pcr number to be extended to "lock" blob
    migratable= 0|1 indicating permission to reseal to new PCR values,
                default 1 (resealing allowed)

keyctl print返回密封密钥的ascii十六进制副本,该副本采用标准TPM\U存储的数据格式。新密钥的密钥长度始终以字节为单位。受信任密钥可以是32-128字节(256-1024位),上限是在2048位SRK(RSA)密钥长度内,并具有所有必要的结构/填充。

加密密钥不依赖于TPM,并且速度更快,因为它们使用AES进行加密/解密。新密钥是从内核生成的随机数创建的,并使用指定的“主”密钥进行加密/解密。“主”密钥可以是受信任的密钥或用户密钥类型。加密密钥的主要缺点是,如果它们不植根于受信任的密钥,它们的安全性仅与加密它们的用户密钥一样。因此,应以尽可能安全的方式加载主用户密钥,最好在启动初期加载。

加密密钥的解密部分可以包含简单的对称密钥或更复杂的结构。更复杂结构的格式是特定于应用程序的,由“格式”标识。

Usage:
    keyctl add encrypted name "new [format] key-type:master-key-name keylen"
        ring
    keyctl add encrypted name "load hex_blob" ring
    keyctl update keyid "update key-type:master-key-name"

format:= 'default | ecryptfs'
key-type:= 'trusted' | 'user'

创建并保存一个长度为32字节的名为“kmk”的受信任密钥:

$ keyctl add trusted kmk "new 32" @u
440502848

$ keyctl show
Session Keyring
       -3 --alswrv    500   500  keyring: _ses
 97833714 --alswrv    500    -1   \_ keyring: _uid.500
440502848 --alswrv    500   500       \_ trusted: kmk

$ keyctl print 440502848
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

$ keyctl pipe 440502848 > kmk.blob

从保存的blob加载受信任密钥:

$ keyctl add trusted kmk "load `cat kmk.blob`" @u
268728824

$ keyctl print 268728824
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

在新的pcr值下重新密封受信任的密钥:

$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
$ keyctl print 268728824
010100000000002c0002800093c35a09b70fff26e7a98ae786c641e678ec6ffb6b46d805
77c8a6377aed9d3219c6dfec4b23ffe3000001005d37d472ac8a44023fbb3d18583a4f73
d3a076c0858f6f1dcaa39ea0f119911ff03f5406df4f7f27f41da8d7194f45c9f4e00f2e
df449f266253aa3f52e55c53de147773e00f0f9aca86c64d94c95382265968c354c5eab4
9638c5ae99c89de1e0997242edfb0b501744e11ff9762dfd951cffd93227cc513384e7e6
e782c29435c7ec2edafaa2f4c1fe6e7a781b59549ff5296371b42133777dcc5b8b971610
94bc67ede19e43ddb9dc2baacad374a36feaf0314d700af0a65c164b7082401740e489c9
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8

受信任密钥的最初使用者是EVM,它在启动时需要一个高质量的对称密钥来保护文件元数据。使用可信密钥可以有力地保证EVM密钥不会受到用户级问题的危害,并且当密封到特定的启动PCR值时,可以防止启动和脱机攻击。使用上述受信任密钥“kmk”创建并保存加密密钥“evm”:

选项1:省略“格式”

$ keyctl add encrypted evm "new trusted:kmk 32" @u
159771175

选项2:将“格式”明确定义为“默认”

$ keyctl add encrypted evm "new default trusted:kmk 32" @u
159771175

$ keyctl print 159771175
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

$ keyctl pipe 159771175 > evm.blob

从保存的blob加载加密密钥“evm”:

$ keyctl add encrypted evm "load `cat evm.blob`" @u
831684262

$ keyctl print 831684262
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

预计还可以使用受信任的加密密钥,如磁盘和文件加密。特别是定义了新格式“ecryptfs”,以便使用加密密钥装载ecryptfs文件系统。有关使用的更多详细信息,请参阅文件“Documentation/security/keys-ecryptfs”。txt'。

翁凯定
2023-03-14

如何使用计算机的TPM模块加密字节?

取决于您的意图和情况:

  • 你有什么样的TPM(1家庭还是2家庭)?
  • TPM处于什么状态?它被拥有了吗?它被配置了吗?
  • 你的编程语言是什么?
  • 你想加密还是签名?(这在问题的其余部分中含糊不清)
  • 您要加密的数据有多大?
  • 您想使用对称密钥还是非对称密钥?
  • 您想使用TPM上已经存在的密钥,还是让它先创建密钥?
  • 你说的"加密"是指"包装密钥"吗?
  • 是否要将加密数据锁定到系统配置,以便只有当系统回到相同配置时才能解密?
  • 您想要解密需要授权吗?
  • 也许您根本不需要加密,而是将数据存储在TPM中?
  • 如果您将数据存储在TPM中,是否需要授权,或者系统处于特定配置以进行检索?

这些用例中的每一个(还有更多)或其组合都提供了不同的实现路径。把TPM想象成一把瑞士军刀式的加密设备:没有什么你做不到的,但它的多功能性会影响易用性。这个问题在加密、签名和锁定系统配置之间不断跳跃,但这个答案的主要部分将考虑使用Seal命令来满足问题中描述的大部分需求。

现在是时候让这更进一步了。我想加密一些只能由本地TPM解密的数据(例如硬盘加密主密钥)。

这就是Bind命令的作用(被TPM 2的Create命令取代)。加载从TPM绑定密钥派生的密钥并使用它进行加密(或直接使用硬件绑定密钥)。这样,只有访问同一TPM才能解密数据。

换言之,我想用Windows中的TPM替换下面框图中针对Android的高通可信执行环境(TEE):

不确定复制整个过程是否是个好主意。首先,没有必要在过程中的任何地方使用签名操作。在开发Android 5的时候,Keystore API似乎仅限于签名和验证操作。我最好的猜测是磁盘加密团队尽了最大努力使用他们所拥有的并设计了一种算法,其中一个中间密钥是通过签名操作导出的,使用存储的TEE密钥,从而将整个过程绑定到只有平台上可用的硬件绑定密钥——因为签名是当时唯一的方法。然而,如果有机会使用TPM,就没有必要以这种方式约束自己,它会给你比你知道你需要的更多的能力!

我意识到TPM不进行数据签名

这是错误的,两个版本的TPM都支持签名。

(或者,如果是这样,则不能保证每次对相同的数据进行签名都会得到相同的二进制输出)

这毫无意义。使用相同的密钥对相同的数据签名将产生相同的签名。您可能会混淆签名操作和报价操作,这将在一段时间内混合使用。

这就是为什么我愿意将“RSA签名”替换为“使用硬件绑定密钥加密256位blob”。

这实际上应该是首选选项,尽管两者都可以通过TPM实现。见上文。

问题是TPM编程在MSDN上完全没有记录。没有可用于执行任何操作的API。

不幸的是,没有太多可记录的内容。Win API仅限于从驱动程序中删除一个级别的几个TBS函数。

相反,您必须找到可信计算组的软件堆栈(又名TSS)的副本,找出要发送到TPM的命令、有效载荷、顺序,并调用Window的Tbsip\u Submit\u Command函数直接提交命令:

实际上,不,如果你有一个TSS,你就不必使用Tbsip_submit_Command()。这就是拥有一个TSS的全部意义——低级细节被抽象掉了。

Windows没有更高级别的API来执行操作。

对于TPM 1仍然如此,但对于TPM 2,存在TSS。MSR。

这在道义上相当于试图通过向硬盘发出SATA I/O命令来创建文本文件。

对的

为什么不直接使用裤子...该代码的问题是它不能移植到Windows世界中。例如,您不能从Delphi使用它,也不能从C#使用它。它需要:OpenSSL、pThread

目前尚不清楚这是否是一个无法克服的挑战。通过互操作访问裤子应该比重写所有数据结构代码更好。此外,在撰写问题时,还有doTSS。

使用TPM加密数据的等效代码是什么?它可能涉及TPM\U seal命令。虽然我不想密封数据,但我想我想绑定它:

这个问题包含一段描述这两个命令之间差异的引文,因此不应该有太多混淆。密封类似于绑定,增加了一个约束,即系统状态必须相同,才能打开数据。

以同样的方式,我能够提供:

Byte[] ProtectBytes_Crypt(Byte[] plaintext)
{
   //...
   CryptProtectData(...); 
   //...
}

是否有人可以提供相应的等价物:

Byte[] ProtectBytes_TPM(Byte[] plaintext)
{
   //...
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   //...snip...
   Tbsip_Submit_Command(...);
   //...
}

除了系统LSA中锁定的钥匙被锁定在TPM中之外,这做了同样的事情?

首先,值得指出的是,TPM有两个主要版本,它们之间完全不兼容。因此,实际上,您为TPM 1编写的任何代码都不能用于TPM 2。TBS API是两者之间唯一的通用代码,公平地说,这可能是该API从未增长的原因之一。答案的主要部分将显示TPM 1的代码,原因有两个:

  • 这个问题包含了TPM 1特定的概念,因此使用TPM 1的人更可能在这里搜索这些概念
  • 有一个针对TPM 2的TSS的Microsoft实现

第二,让我们把问题说得更具体一些。我将其重新解释如下:

How do I write code in C#, using only the TBS API, to interface with
an already owned and provisioned TPM to, without user interaction,
encrypt no more than 128 bytes of arbitrary data with an asymmetric
key already resident in the TPM and bound to it, but not protected
with a password, so that in order to decrypt the data the system may
need to be in the same state it was in at encryption time based on an
easily configurable variable?

Seal命令最适合于此,因为当PCR选择大小设置为零时,它执行与Bind命令相同的功能,但可以轻松更改PCR选择以包含您可能想要的任何PCR。这让人想知道为什么Bind命令会包含在规范中,并且如前所述,它在TPM 2规范中被删除,并且两者合并在一个创建命令中。

以下是使用TPM 1.2 Seal命令仅使用TBS函数加密数据的C#代码(注意:此代码未经测试,未经调试可能无法工作):

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsi_Context_Create (UInt32 * version, IntPtr * hContext);

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsip_Context_Close (IntPtr hContext);

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsip_Submit_Command (
    IntPtr hContext, UInt32 Locality, 
    UInt32 Priority, 
    byte * pCommandBuf, 
    UInt32 CommandBufLen, 
    byte * pResultBuf, 
    UInt32 * pResultBufLen);

byte[] ProtectBytes_TPM (byte[] plaintext) {

    void AddUInt32Reversed (byte[] a, System.UInt32 o, ref int i) {
        byte[] bytes = System.BitConverter.GetBytes (o);
        Array.Reverse (bytes);
        Array.Copy (bytes, 0, a, i, bytes.Length);
        i += bytes.Length;
    }
    void AddUInt16Reversed (byte[] a, System.UInt16 o, ref int i) {
        byte[] bytes = System.BitConverter.GetBytes (o);
        Array.Reverse (bytes);
        Array.Copy (bytes, 0, a, i, bytes.Length);
        i += bytes.Length;
    }
    void AddBool (byte[] a, byte b, ref int i) {
        a[i] = b;
        i += 1;
    }
    void AddBlob (byte[] a, byte[] b, ref int i) {
        Array.Copy (b, 0, a, i, b.Length);
        i += b.Length;
    }
    byte[] Xor (byte[] text, byte[] key) {
        byte[] xor = new byte[text.Length];
        for (int i = 0; i < text.Length; i++) {
            xor[i] = (byte) (text[i] ^ key[i % key.Length]);
        }
        return xor;
    }

    int offset;

    Random rnd = new Random ();

    IntPtr hContext = IntPtr.Zero;
    unsafe {
        UInt32 version = 1;
        IntPtr handle = hContext;
        UInt32 result = Tbsi_Context_Create ( & version, & handle);

        if (result == 0) {
            hContext = handle;
        }
    }

    byte[] cmdBuf = new byte[768];

    //OSAP
    System.UInt32 outSize;

    byte[] oddOsap = new byte[20];
    byte[] evenOsap = new byte[20];
    byte[] nonceEven = new byte[20];
    byte[] nonceOdd = new byte[20];
    System.UInt32 hAuth = 0;

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C1, ref offset);
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x0000000B, ref offset);

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt16Reversed (cmdBuf, 0x0004, ref offset); //Entity Type SRK = 0x0004
    AddUInt32Reversed (cmdBuf, 0x40000000, ref offset); //Entity Value SRK = 0x40000000
    rnd.NextBytes (oddOsap);
    AddBlob (cmdBuf, oddOsap, ref offset);
    uint cmdSize = (System.UInt32) offset;
    offset = 2;
    AddUInt32Reversed (cmdBuf, cmdSize, ref offset);

    outSize = (System.UInt32) (Marshal.SizeOf (hAuth) + nonceEven.Length + evenOsap.Length);

    byte[] response = new byte[outSize];
    unsafe {
        UInt32 result = 0;

        //uint cmdSize = (uint)offset;
        uint resSize = outSize;
        fixed (byte * pCmd = cmdBuf, pRes = response) {
            result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);
        }
    }

    byte contSession = 0;
    System.UInt32 hKey = 0x40000000; //TPM_KH_SRK;
    System.UInt32 pcrInfoSize = 0;
    byte[] srkAuthdata = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    uint inDataSize = (uint) plaintext.Length;

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for return code
    byte[] hauthbytes = new byte[Marshal.SizeOf (hAuth)];
    Array.Copy (response, offset, hauthbytes, 0, hauthbytes.Length);
    Array.Reverse (hauthbytes);
    hAuth = System.BitConverter.ToUInt32 (hauthbytes, 0);
    offset += Marshal.SizeOf (hAuth);
    Array.Copy (response, offset, nonceEven, 0, nonceEven.Length);
    offset += nonceEven.Length;
    Array.Copy (response, offset, evenOsap, 0, evenOsap.Length);

    //shared-secret = HMAC(srk_auth, even_osap || odd_osap)
    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

    byte[] authSha1InBuf = new byte[sharedSecret.Length + nonceEven.Length];
    Array.Copy (sharedSecret, 0, authSha1InBuf, 0, sharedSecret.Length);
    Array.Copy (nonceEven, 0, authSha1InBuf, sharedSecret.Length, nonceEven.Length);
    System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed ();
    byte[] authSha1 = sha1.ComputeHash (authSha1InBuf);
    byte[] encAuth = Xor (srkAuthdata, authSha1);

    //inParamDigest = sha1(1S ~ 6S) 
    int paramInDigestInBufSize =
        sizeof (System.UInt32) + 
        encAuth.Length +
        Marshal.SizeOf (pcrInfoSize) +
        Marshal.SizeOf (inDataSize) +
        (int) inDataSize;
    byte[] paramInDigestInBuf = new byte[paramInDigestInBufSize];
    offset = 0;
    AddUInt32Reversed (paramInDigestInBuf, 0x00000017, ref offset);
    AddBlob (paramInDigestInBuf, encAuth, ref offset);
    AddUInt32Reversed (paramInDigestInBuf, 0x0, ref offset); //PCR info size
    AddUInt32Reversed (paramInDigestInBuf, inDataSize, ref offset);
    AddBlob (paramInDigestInBuf, plaintext, ref offset);

    byte[] paramInDigest = sha1.ComputeHash (paramInDigestInBuf);

    int pubAuthInBufSize = paramInDigest.Length + nonceEven.Length + nonceOdd.Length + Marshal.SizeOf (contSession);
    byte[] pubAuthInBuf = new byte[pubAuthInBufSize];

    offset = 0;
    AddBlob (pubAuthInBuf, paramInDigest, ref offset);
    AddBlob (pubAuthInBuf, nonceEven, ref offset);
    AddBlob (pubAuthInBuf, nonceOdd, ref offset);
    AddBool (pubAuthInBuf, contSession, ref offset);
    System.Security.Cryptography.HMACSHA1 pubAuthHmac = new System.Security.Cryptography.HMACSHA1 (sharedSecret);
    byte[] pubAuth = pubAuthHmac.ComputeHash (pubAuthInBuf);

    //Seal
    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C2, ref offset); // TPM_TAG_RQU_AUTH1_COMMAND;
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x00000017, ref offset); // TPM_ORD_SEAL;
    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt32Reversed (cmdBuf, hKey, ref offset);
    AddBlob (cmdBuf, encAuth, ref offset);
    AddUInt32Reversed (cmdBuf, pcrInfoSize, ref offset);
    AddUInt32Reversed (cmdBuf, inDataSize, ref offset);
    AddBlob (cmdBuf, plaintext, ref offset);

    AddUInt32Reversed (cmdBuf, hAuth, ref offset);
    AddBlob (cmdBuf, nonceOdd, ref offset);
    AddBool (cmdBuf, contSession, ref offset);
    AddBlob (cmdBuf, pubAuth, ref offset);
    cmdSize = (System.UInt32) offset;
    offset = 2;
    AddUInt32Reversed (cmdBuf, cmdSize, ref offset);

    outSize = 768;
    uint responseSize = 0;

    response = new byte[outSize];
    unsafe {
        UInt32 result = 0;

        uint resSize = outSize;
        fixed (byte * pCmd = cmdBuf, pRes = response) {
            result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);
        }
        responseSize = resSize;
    }

    byte[] retBuffer = new byte[responseSize - 10];
    Array.Copy (response, 10, retBuffer, 0, retBuffer.Length);
    Tbsip_Context_Close (hContext);
    return retBuffer;

}

代码分析:

[DllImport ("tbs.dll")]
...

这些是Tbs中为数不多的可用功能之一。h和我们在这里唯一使用的。它们基本上允许您打开设备的句柄,并通过发送和接收原始字节与之通信。

    void AddUInt32Reversed (byte[] a, System.UInt32 o, ref int i) { ... }
    void AddUInt16Reversed (byte[] a, System.UInt16 o, ref int i) { ... }
    void AddBool (byte[] a, byte b, ref int i) { ... }
    void AddBlob (byte[] a, byte[] b, ref int i) { ... }

TPM是big-endian,Windows是little-endian。因此,对于我们发送的任何数据,字节顺序都必须颠倒。我们只需要担心在这里反转32位和16位无符号整数。

    ...
    UInt32 result = Tbsi_Context_Create ( & version, & handle);
    ...

这里,我们使用Tbsi\u Context\u Create()打开一个句柄来与TPM对话。TBS\u CONTEXT\u PARAMS参数只是一个C结构,它有一个无符号的32位int字段,必须设置为1才能与TPM 1.2实例通信,我们将其设置为1。

    byte[] cmdBuf = new byte[768];

这是TPM PC客户端规范中指定的最小缓冲区大小。这将足以满足我们的需要。

TPM 1.2规范第3部分规定如下:

TPM_Seal requires the encryption of one parameter (“Secret”). For the
sake of uniformity with other commands that require the encryption of
more than one parameter, the string used for XOR encryption is
generated by concatenating a nonce (created during the OSAP session)
with the session shared secret and then hashing the result.

我们需要使用OSAP会话期间生成的nonce对这个“secret”参数进行异或加密。Seal命令输入句柄之一也是OSAP句柄:

The authorization session handle used for keyHandle authorization.
Must be an OSAP session for this command.

因此,我们需要首先建立OSAP会话。TPM 1.2规范第1部分描述了OSAP。OSAP,或特定于对象的授权协议,被发明用来处理您想要多次使用需要授权的TPM对象,但不想每次都提供授权的用例:而是使用OSAP会话,它依赖于“共享机密”的概念,这是一种HMAC,它将对象授权数据与每侧生成的nonce混合,以防止应答攻击。因此,“共享机密”仅在此会话中为双方所知:发起会话的一方(用户)和接受会话的一方(TPM);此外,双方必须具有相同的对象授权数据,才能使“共享机密”相同;此外,在一个会话中使用的“共享机密”在另一个会话中将无效。规范中的此图描述了该过程:

在这种特殊情况下,我们不会使用多个会话(事实上,Seal命令会忽略该参数!)我们将使用的密钥不需要授权,但不幸的是,我们仍受规范的约束,无法建立OSAP会话。

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C1, ref offset);
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x0000000B, ref offset);

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt16Reversed (cmdBuf, 0x0004, ref offset); //Entity Type SRK = 0x0004
    AddUInt32Reversed (cmdBuf, 0x40000000, ref offset); //Entity Value SRK = 0x40000000
    rnd.NextBytes (oddOsap);
    AddBlob (cmdBuf, oddOsap, ref offset);
    uint cmdSize = (System.UInt32) offset;

TPM_OSAP命令操作数是:

每个TPM 1.2命令的布局如下:

  2 bytes       4 bytes             4 bytes
+---------+------------------+------------------+---------------------------
|   Tag   |       Size       |   Command code   |    Command body    ....
+---------+------------------+------------------+---------------------------

标记是一个两字节的值,它指示后面是输入还是输出,以及命令参数后面是否有任何身份验证数据值。对于TPM\U OSAP,根据规范,标记必须是TPM\U tag\U RQU\U命令(0x00C1),这意味着“没有授权的命令”。

size是一个四字节的值,它以字节为单位指定命令的大小,包括标签和size本身。一旦我们计算出这个值,我们将在以后设置它。

命令代码是一个四字节的值,作为命令ID提供:它告诉TPM如何解释命令的其余部分。这里的命令代码是TPM\U OSAP(0x0000000B)。

接下来要设置的两件事是实体类型和实体值。由于我们希望使用TPM中已经存在的密钥,我们将使用实体类型“SRK”(0x0004),并且由于我们在假设TPM已经拥有的情况下工作,因此可以安全地假设它有一个SRK,根据规范,它加载在永久句柄0x40000000下,因此我们将使用此永久句柄值作为实体值。(SRK代表“存储根密钥”,是大多数其他TPM拥有的密钥派生的根密钥)

    result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);

最后我们计算命令大小并设置它,然后发送命令。

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for return code
    byte[] hauthbytes = new byte[Marshal.SizeOf (hAuth)];
    Array.Copy (response, offset, hauthbytes, 0, hauthbytes.Length);
    Array.Reverse (hauthbytes);
    hAuth = System.BitConverter.ToUInt32 (hauthbytes, 0);
    offset += Marshal.SizeOf (hAuth);
    Array.Copy (response, offset, nonceEven, 0, nonceEven.Length);
    offset += nonceEven.Length;
    Array.Copy (response, offset, evenOsap, 0, evenOsap.Length);

我们应该在TPM\U OSAP上从TPM获取的数据是:

所以我们回来了:

  • 与我们的主命令一起使用的授权句柄(Seal)
  • nonce甚至:TPM生成的与main命令一起使用的随机数
  • nonceEvenOSAP:OSAP随机数,它是我们在发送TPM_OSAP命令之前在我们这边生成的随机数的反随机数。这两个随机数将用于生成“共享秘密”。

我们提取这些值并将其存储在变量中。

    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

然后计算“共享秘密”。根据规范,进入计算的值是两个OSAP nonce(一个由用户生成,一个由TPM生成)和我们要使用的密钥的授权值——SRK。按照惯例,SRK auth值是“众所周知的auth”:一个清零的20字节缓冲区。从技术上讲,在获得TPM所有权时,可以将此值更改为其他值,但实际上并没有这样做,因此我们可以放心地假设“知名身份验证”值是好的。

接下来,让我们看看TPM\U Seal命令中的内容:

这些参数中的大多数对于构建来说都是微不足道的,除了其中的两个:encAuthpubAuth。让我们一个接一个地看它们。

encAuth是“密封数据的加密AuthData”。我们这里的AuthData是以前的“众所周知的身份验证”,但是的,我们仍然必须对其进行加密。因为我们使用的是OSAP会话,所以它是按照ADIP或授权数据插入协议进行加密的。从规范来看:“ADIP允许创建新实体和安全插入新实体AuthData。新AuthData的传输使用基于OSAP会话共享秘密的密钥进行加密。”此外:“对于强制异或加密算法,创建者使用O共享SAP秘密的SHA-1哈希和会话随机数构建加密密钥。创建者异或使用加密密钥作为一次性垫加密新的AuthData,并将加密数据连同创建请求一起发送给TPM。因此,我们必须从会话随机数和“共享秘密”中构建一个异或密钥,然后用该密钥异或加密我们的“众所周知的身份验证”。

下图说明了ADIP的操作方式:

pubAuth是“输入和密钥句柄的授权会话摘要”规范第1部分,在“OIAP和OSAP示例的参数声明”中,解释了如何解释上面的TPM\U密封参数表:“HMAC#列详细说明了HMAC计算中使用的参数。参数1S、2S等被串联并散列为inParamDigest或outParamDigest,如果有两个授权会话,则隐式称为1H1和1H2。对于第一个会话,1H1、2H1、3H1和4H1被串联并HMAC'ed。对于第二个会话,1H2、2H2、3H2和4H2被串联并HMAC'ed。”因此,我们必须散列上面的明文、其大小、PCR信息大小、encAuth和TPM\U Seal序号,然后使用OSAP“shared secret”作为HMAC密钥,使用两个nonce和“continue session”布尔值对HMAC进行散列。

将其全部放在一个图表中:

请注意,在这段代码中,我们如何将“PCR info size”设置为零,因为我们只想加密数据,而不将其锁定到系统状态。然而,如果需要的话,提供“PCR信息”结构并不重要。

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C2, ref offset); 
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x00000017, ref offset); // TPM_ORD_SEAL;
    ...
    result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);

最后,我们构造命令并发送它。

    byte[] retBuffer = new byte[responseSize - 10];
    Array.Copy (response, 10, retBuffer, 0, retBuffer.Length);
    Tbsip_Context_Close (hContext);
    return retBuffer;

我们使用Tbsip\u Context\u Close()函数关闭通信句柄。

我们返回此处的响应。理想情况下,您希望再次反转字节,并通过重新计算resAuth值对其进行验证,以防止中间人攻击。

令人困惑的是,没有Tspi\U Data\U Bind命令。

这是因为Tspi_Data_Bind是TSS命令,而不是TPM命令。原因是因为它不需要秘密(只使用公钥),所以可以在不涉及TPM的情况下完成。然而,这引起了混乱,甚至不需要秘密的命令现在也包含在TPM 2规范中。

如何使用TPM的公钥加密密钥?

取决于TPM版本。使用TPM 1.2的TPM\u CreateWrapKey命令。使用TPM 2的TPM2\U Create命令。

开发人员如何锁定TPM的密钥?

要么在TPM中创建它,要么包装它,要么使用任何其他可用的方法。

TPM2_Create,指定HMAC密钥

书中的文字令人困惑。您没有指定HMAC密钥,而是指定需要HMAC密钥。

HMAC密钥不是秘密这一事实很有道理

不,这没有道理。钥匙是秘密的。

...使用钥匙,同时将它们安全地保存在硬件设备中...太棒了!你是怎么做到的!?

对于这两个版本的TPM,都有创建密钥或导入密钥的命令。对于TPM 1,只有一个根密钥—SRK—您可以通过创建打包密钥从中建立密钥层次结构。使用TPM 2,您可以有多个主密钥或根密钥。

TPM是否有能力在硬件边界内生成加密密钥并保护其机密?是这样吗,怎么做?

见上文。

太棒了!这正是我碰巧想要的用例。这也是Microsoft使用TPM的用例。我该怎么做!?

可能取决于驱动器的类型。对于非SED驱动器,驱动器加密密钥可能用TPM密钥包装。对于SED驱动器,Admin1密码(或类似密码)由TPM密封。

背书密钥或EK。。。TPM中的某个地方有一个RSA私钥。那把钥匙锁在那里,永远不会被外界看到。我希望TPM使用其私钥对某些内容进行签名(即使用其私钥对其进行加密)。

EK不是签名密钥,而是加密密钥。然而,它不是通用加密密钥:它只能在某些上下文中使用。

但我真正想做的是“密封”一些数据

见上文。

阴高寒
2023-03-14

下面是关于TPM 1.2的内容。请记住,对于所有未来的Windows版本,Microsoft都需要TPM 2.0。2.0代与1.2有着根本的不同

由于TPM设计原则,没有单行解决方案。将TPM视为资源有限的微控制器。它的主要设计目标是既便宜又安全。因此,TPM被破坏了安全操作所不需要的所有逻辑。因此,只有当您至少有一些或多或少的fat软件,并以正确的顺序发出大量命令时,TPM才起作用。这些命令序列可能会变得非常复杂。这就是TCG使用定义良好的API指定TSS的原因。如果您想采用Java方式,甚至还有一个高级Java API。我不知道C#/有类似的项目。网

在您的情况下,我建议您看看IBM的软件TPM。

  • 项目页面
  • 不要装载整个包

在软件包中,您将发现3个非常有用的组件:

  • 软件TPM仿真器

您不一定需要软件TPM模拟器,您也可以连接到机器的HW TPM。但是,您可以拦截发出的命令并查看响应,从而了解它们是如何组装的以及它们如何与命令规范相对应。

先决条件:

  1. TPM被激活
  2. TPM驱动程序已加载
  3. 您已获得TPM的所有权

要密封blob,需要执行以下操作:

  1. 创建密钥
  2. 将密钥blob存储在某个地方
  3. 确保密钥在TPM中加载
  4. 密封斑点

要解封,您需要:

  1. 获取密钥blob
  2. 将钥匙加载到TPM
  3. 打开密封的水滴

您可以将密钥blob存储在用于存储受保护字节的数据结构中。

您需要的大多数TPM命令都是经过授权的命令。因此,您需要在需要的地方建立授权会话。AFAIR这些主要是OSAP会议。

目前我无法运行调试版本,因此我无法为您提供确切的序列。因此,将此视为您必须使用的无序命令列表:

  • TPM\U OSAP

如果您也想读取当前的PCR值:

  • TPM_PCRRead
 类似资料:
  • 我的代码如下: 有人来帮我吗

  • 当我偶然发现let's加密时,我开始查看ssl证书,我想将其与gitlab一起使用,但是由于它在树莓pi 2上运行,并且现在运行得非常完美(所以我不想搞砸任何事情),他会让我正确安装let加密ssl证书吗?PS:我的安装是综合的。

  • 我一直试图加密一个PDF使用Java。到目前为止,我可以成功加密其他文件类型(.txt、.png等)。当我用PDF做的时候,当我解密它的时候,它就会破坏信息。 这就是我用来加密它的: 并用这个来解密它: 更新: 这是我用来读取文件的: 使用它重写文件

  • Go-TPM 是一个 Go 开发包,能够在 Linux 平台上直接与 TPM 通信 。TPM 规范中指定的格式能够直接通过缓冲区进行通信 。当前版本支持创建身份认证密钥和 TPM 的所有权等功能。

  • 我有C#中的OpenPGP 和< code>pgpid,如何使用Bouncy Castle库简单地加密一个字符串? 接收方公钥(MPI in base64):< code > BADelitpUqMZLn bryzr 5 rk 9j 3 eu prvfp 5 tpbooliwo 2 vqo/rci 8 vvt 2 tpzejarwhyz 465 niohyciia 9 vague P4 rsdzf

  • 我想在Go中使用AES-256加密一个字符串,无需任何GCM处理,以与MQL4进行比较。当我试图加密特殊字符或数字时,我会遇到问题。我应该以某种方式预处理我的明文吗?我是新来的,所以任何帮助都将不胜感激;我的代码在这个解释下面。 如果我加密明文“这是一个秘密”,然后解密密文(编码为十六进制),我会得到相同的结果(即“这是一个秘密”)。pt是下面代码中明文的变量名。 如果我试图加密“这是一个秘密;1