作为一个安全设备,TPM最强大的功能就是,应用程序在使用密钥的同时保证密钥安全地保存在硬件设备中。TPM可以生成密钥,也可以导入在外部生成的密钥。它支持对称和非对称密钥。第二章已经描述了这两种密钥的基本原理。
因为TPM设备的存储资源有限,必要时应用程序可以安全地将密钥换入换出TPM,这时TPM可以被认为是一个密钥缓存。相关操作会在“密钥缓存”这一节描述。
一共有三种密钥组织架构,它们分别受控于不同的安全角色。每一种组织架构都可以将密钥组织成树形父子关系。第九章已经描述了组织架构和它们的应用。
每一个密钥都有独立的安全控制方式,包括口令,增强的授权策略,密钥复制到其他父节点或者其他TPM的限制,签名或者加解密密钥的用途限制。密钥可以被认证也可以用于认证其他密钥。密钥属性相关的内容将会在“密钥类型和属性”这一节中讨论。适用于所有TPM实体的授权操作细节,包括口令和policy,将会被推迟到第13,14章介绍。
下面介绍经常用于密钥操作的TPM命令。需要说明的是,下面的列表并不是完整的命令。参考TPM2.0规范第3部分来了解完整的命令集和API细节。这些命令将按照后续章节中描述和应用案例中出现的顺序来介绍:
- TPM2_Create和TPM2_CreatePrimary可以通过模板创建所有类型的密钥。
- TPM2_Load(用于加密的私钥)和TPM2_LoadExternal(用于公钥或者明文私钥)可以将密钥加载到TPM中。
- TPM2_ContextSave和TPM2_ContextLoad用于将密钥换入换出TPM缓存。TPM2_FlushContext用于删除TPM中的密钥。TPM2_EvictControl可以让一个密钥持续存在与TPM中或者删除一个持续存在的密钥。这些函数及其应用将会在18章详细介绍。
- TPM2_Unseal,TPM2_RSA_Encrypt,和TPM2_RSA_Decrypt使用加密密钥完成相关操作。
- TPM2_HMAC,TPM2_HMAC_Start,TPM2_SequenceUpdate,和TPM2_SequenceCompete使用对称签名密钥和HMAC算法完成相关操作。
- TPM2_Sign是一个通用的签名命令,TPM2_VerifySignature用于验证数字签名。
- TPM2_Certify,TPM2_Quote,TPM2_GetSessionAuditDigest,TPM_GetTime是用于对认证数据签名的特殊命令。具体来讲,TPM2_Certify可以实现一个密钥签名另外一个密钥(密钥的名称)。这样一来,TPM就可以作为一个证书授权机构,使用自己的密钥认证证书相关密钥的属性。
理论上来说,TPM最强大的能力是,它能产生密钥并将相关的秘密信息安全地保存在硬件中。密钥生成器基于TPM自己的随机数发生器,它不依赖任何外部的随机源。因此,它消除了由较弱的软件随机数生成器或者不充足的熵值带来的弱点。
TPM的密钥可以组成一个等级架构,在这个架构中父节点的密钥用于加密子节点的密钥。主密钥就是这个密钥组织架构中的根密钥。它们没有父节点。第9章讨论了组织架构的通用概念及其应用。后续再“密钥组织架构”这一节我们将重点讨论一些密钥相关的组织架构。
这一小结将按照主密钥生成和销毁的顺序讨论。在本节叙述中,调用者表示对TPM做初始配置,发送命令和接收命令响应的软件;TPM则是执行命令的硬件设备。配置软件(参考第19章)通常会执行上述步骤。用户可能会使用主密钥,但是通常他们不会创建这些主密钥。
主密钥的创建命令使用易于理解的命名方式TPM2_CreatePrimary。如果你熟悉TP1.2,你就知道TPM1.2中有一个和TPM2.0主密钥相同的密钥:根存储密钥(SRK),这个密钥会一直存在于TPM中。TPM2.0允许有无限多个主密钥,但是它们并不一定一直存在于TPM中。你可能会说,因为TPM的永久性存储空间有限,所以密钥的数量也不可能是无限多的,事实不是这样的。主种子的存在可以支持无限多主密钥。
TPM1.2在只有一个密钥的情况仍然可以工作的原因有两个。首先,它只有一个密钥算法和一个密钥大小用于加密密钥,那就是RSA-2048。然而TPM2.0中支持多种密钥算法和多种密钥大小。其次,TPM1.2只有一个密钥组织架构:存储组织架构。TPM2.0有三种组织架构,每一种都至少有一个根节点。第9章已经介绍过组织架构的一般概念和它们的应用案例。
那么,究竟TPM是怎么用有限的非易失性存储空间来实现不限数量的根密钥的呢?要知道的是,一个根密钥不能存储在TPM之外的,因为它没有父密钥来加密它的私有部分。答案就是主密钥种子。
三个组织架构的每一个都和一个主密钥种子关联,分别是:背书主密钥种子,平台主密钥种子,和存储主密钥种子。这些种子一直存在于TPM设备中。它们就是密钥生成函数输入的秘密信息。当TPM创建一个主密钥时,它使用主密钥种子和一个公共模板来生成密钥。密钥模板包含了所有的密钥配置信息:密码算法和密钥长度,密钥的policy,密钥的类型(签名,加密等等)。调用者还可以在模板中添加自己的独有数据。都有数据在模板的公钥区域中添加。
密钥生成函数时固定且可重复的。对于相同的种子来说,相同的密钥模板总是会产生相同的密钥。通过改变独有数据,调用者可以创建无限多个主密钥。
当TPM创建好一个主密钥后,密钥就存储在TPM的易失性内存中。这时候用户有两种选择。通过TPM2_EvictControl将有限数量的主密钥转到非易失性的内存空间。剩下的主密钥继续保存在易失性内存中。
如果所需的主密钥数量多于TPM可以保存到持续性存储空间和易失性存储空间中的密钥数量,可以选择性地将密钥从易失性内存中清除,或者先将非易失性存储空间中的密钥移动到易失性存储空间,然后再清除。因为密钥种子时永久性的,所以密钥永远不会丢失。因为如果调用者知道可能是完全公开的密钥模板,TPM就可以在需要的时候重新创建一个完全一样的密钥。进一步讲,如果重新创建的密钥是RSA密钥,那这个过程可能需要很长时间。如果重新创建的密钥是椭圆曲线,AES,或者HMAC密钥,创建的过程就会非常快。在大多数的应用场景中,在存储组织架构下至少有一个主存储密钥是持续性存在的,它的角色和SRK类似。
在实际应用中上述的功能具体是怎样工作的呢?在TPM1.2中,有一个背书密钥和与这个密钥相关的TPM厂商签名过的证书。他们被存储在非易失性内存中,这样一来最终的用户使用包含TPM设备的系统时,它们也就同样拥有了这个背书密钥的证书,证书和密钥通常被存储在TPM的NVRAM中。在TPM2.0中,用户可以拥有多个密钥/证书对儿,TPM实现的每一中算法至少一对。但是,用户可能不想使用珍贵的持续性存储空间用户存储密钥和证书,尽管TPM可以这样做。
既然不想浪费持续性存储空间,那应该怎么办呢?一个可能的,同时也是TPM厂商期望的解决方法是,让TPM生产商使用背书密钥种子生成几个背书主密钥钥和相应的证书,密钥使用标准算法集和广为人知的模板。其中的一种密钥及其证书,比如流行的RSA-2048被存储到持续性内存中。厂商将剩下的密钥清除,但是保存相应的证书。
TCG的基础设施工作组已经定义了一些背书主密钥的模板。RSA模板使用RSA-2048,SHA-256,和AES-128。ECC模板使用NIST-P256曲线,SHA-256,和AES-128。这两种模板使用相同的授权策略,这个授权策略要求知道背书组织架构的口令。这个策略代理了背书组织架构管理员的密钥授权。模板的独有数据部分是空的。密钥属性是(参看“密钥类型和属性”小节)fixedTPM和fixedParent都为真,也就是背书密钥被期望的那样,不能被复制。userWithAuth和adminWithPolicy被指定到Policy中,这样一来密钥必须使用policy授权,而不是口令。这样也是合理的,因为TPM厂商没有办法向用户传递口令。密钥的类型是restricted decryptkey:也就是说,是一个存储密钥。
基于前面的描述,假设用户想要一个不同的主密钥。他可以将TPM厂商预置在TPM中的密钥清除,然后自己选择算法重新生成一个主密钥。
这时候神奇的事情发生了!因为密钥的种子没有变化,并且用户重新生成密钥的时候使用的模板与TPM厂商使用的相同,所以用户得到的密钥和TPM厂商之前生成的密钥相同。用户可以把密钥的公钥部分当作TPM厂商证书列表的索引。这个证书可以存储在一个公共的服务器上。这样用户就可以方便的访问证书并开始使用。这种可重复的密钥生成方式允许TPM厂商在生产TPM时就预先生成许多密钥及其证书,但是不用将所有的密钥都存储到非易失性内存中。最终的用户需要的时候可以重新生成。
需要注意的是,TPM厂商必须实现生成所有可能需要的主密钥和厂商办法的主密钥证书。种子是一种秘密信息,因为如果不是的话,厂商就不能确定一个公钥来自于厂商自己生产的TPM设备。
一旦种子被修改,主密钥就永远不能再重新生成了,TPM中所有基于这个种子的密钥都被认为是以清除的无效的。种子被修改也意味着所有厂商生成的证书也都将没有意义。因为TPM厂商为一个新的TPM背书密钥生成证书非常困难(因为这个过程通常是厂商在量产阶段批量生成的,线下可能需要特殊复杂的流程),所以修改背书组织架构的种子受控于平台组织架构,这里的平台组织架构通常是指OEM厂商。这也就意味着最终的用户很难修改这个种子。但是从另一方面讲,只要用户在密钥模板中的随便输入一些独有数据,他就可以创建和TPM厂商完全没有关系的,自己独立的背书密钥。这未尝不是一件好事。
应用案例:多个主密钥
用户可以拥有多个主存储密钥作为密钥组织架构的根节点。但是这些密钥不能全部存储在非易失性内存中。如果用户使用大家熟知的模板创建密钥,他可以在需要的时候重新创建这些密钥。
以上相关的TPM密令如下:
* TPM2_NV_Read:从TPM的NV区域中读取熟知的密钥模板。TPM厂商可能会事先配置几种模板(比如说,一个RSA和一个ECC),这些模板和厂商配置的证书相匹配。用户也有可能有公司级别的模板。
* TPM2_CreatePrimary:需要选择模板。
* TPM2_EvictControl:可以选择性地将密钥配置成持续存在于TPM中。尤其是对于RSA密钥来说,这样就能节省重新生成密钥的时间(前面提到了,重新生成这种密钥很费时间)。当然密钥也可以留在易失性内存种,每次上电以后重新生成它们。
用户可以通过TPM2_EvictControl命令将一个密钥有易失性内存转移到非易失性内存中,这样一来密钥就在两个上电周期之间保持加载状态。但是密钥的使用不需要将它变成非易失性的。通常情况下,我们只希望有一小部分主密钥,可能是一个组织架构一个,被转成持续性的,这样就可以省去重新生成密钥的时间,从而提升性能。
需要说明的是,背书,存储,和平台组织架构下除主密钥意外的其他密钥也可以被设置成持续性的。一个典型的应用案例是,在系统启动初期时硬盘不可用,但是这时候需要一个密钥。另外一个应用场景就是在资源受限的平台上,比如说嵌入式控制器,它可能没有外部的持续性非易失性的存储空间。
NULL组织架构下的密钥都不能被设置成持续性的。它们在重启后被清除。
虽然只有有限数量的密钥可以被设置成持续性的,但是TPM可以处理理论上无限多的密钥。因为应用程序将TPM当作密钥缓存来使用。
对于不是主密钥的其他密钥来说,TPM就像是一个密钥缓存。具体来说,TPM2_Create命令创建一个密钥后,使用这个密钥的父密钥加密,然后向调用者返回加密过的密钥。用户会将密钥存储在TPM之外,可能是硬盘中。当用户需要使用这个密钥时,他必须使用TPM2_load密令将密钥加载到它的父密钥下。使用完成以后,用户可以使用TPM2_FlushContext命令将密钥从TPM内存中清除。这个使用过程与主密钥不同,主密钥没有父节点,他被创建以后会暂时保留在TPM中。
一个典型的硬件TPM可能会有5-10个密钥位置(槽,key slots):密钥槽就是密钥可以加载到的TPM内存空间。TPM管理中间件负责将密钥换入换出密钥缓存。
如果你阅读第13章,你可能会发现密钥的handle没有包含在TPM授权参数中。相反授权使用的是名称。原因就是密钥缓存和密钥换入换入操作。一个平台可能有大量的应用软件相关的密钥存储在磁盘上,它们可能通过用户的handle来识别。但是这样一来handle的数量就会远远比TPM密钥槽的数量要多。当管理中间件重新加载一个密钥后,它可能会得到一个不同的handle,这个handle可能是和TPM密钥槽的空闲状态相关的,而不是用户初始的handle。因此,中间件必须替换用户handle。如果授权数据中包含了handle信息,那中间件替换handle将会导致授权失败。
你还可能会问,如果handle可以被替换,那如果我有两个密钥的授权秘密信息相同,我怎么知道中间件没有非法使用跟我想要用的密钥不同的密钥呢?这确实是TP1.2的一个潜在问题。
TPM2.0通过将名称添加到授权数据中来解决这个问题,名称就是密钥公钥部分的摘要值。这样一来中间件可以修改密钥的handle,但是不同改变密钥的名称。
根密钥和密钥缓存共同组成一个密钥树。TPM提供了四种这样的树,每一种都有不同的控制角色。这些树也叫做组织架构(等级结构)。
尽管TPM对密钥做硬件保护已相对软件生成密钥来说已经是很大的提高了,它仍然在此基础上提供很强的密钥访问控制功能。一个软件生成的密钥经常通过使用口令做访问控制来保护密钥。比如说,一个密钥可能会用口令来加密。这种保护的强度与口令本身的强度一样,所以这个密钥很容遭受线下的暴力攻击。也就是说,一旦攻击者拿到了加密过的密钥,解密这个密钥就变成了破解用于加密它的口令。密钥的所有者不能阻止一个高频率尝试口令的攻击方法。并且这种攻击可以被并行化,也就是说不同的机器同时使用不同的口令来实施破解。云服务已经让这种攻击变得非常容易了。
TPM针对软件生成的密钥做了两方面的改进。首先,当密钥离开TPM时,它会被一个强度很高的父密钥加密。这个时候攻击者需要破解一个强度很高的密钥而不是一个口令。其次,当密钥被加载到TPM中时,它还会收到字典攻击防护逻辑的保护。每一次攻击者尝试授权密钥失败时,这个逻辑就会记录下来。当失败的次数达到一定预置值时,TPM就会阻止密钥授权,并保持一段事先配置好的时间。这将很可能大大降低攻击者实施攻击的频率。这种频率限制机制可以保证即使破解一个很弱的口令也要耗费比破解软件密钥长得多的时间,因为软件的密钥没有尝试频率限制。第13章会详细介绍HMAC和口令授权。
TPM提供了许多比简单口令更丰富的访问控制机制。但是,最终还是基于硬件的字典攻击防护机制使得一个TPM密钥口令免于遭受攻击。
有些时候密钥需要被销毁。因为有可能密钥的授权信息已经被泄漏了。也有可能密钥所在的机器已经另作他用了。存储在软件中的密钥几乎不能被销毁,因为这些软件可能已经被到处拷贝了。但是TPM的密钥有父密钥或者是密钥,所以它们可以被销毁。
就像第9章中描述的那样,TPM有三种持续性的组织架构(背书,存储,平台)和一个易失性的组织架构(空组织架构)。每一个架构都有它独有的主密钥种子。把主密钥种子擦出就可以阻止在相应的阻止架构下重新创建主密钥,当然这是一个影响重大并且很少做的操作。擦除主密钥就可以阻止所有它的子密钥被加载到TPM中。任何属性配置为必须存在于TPM中的密钥也都被认为是销毁了。
一个组织架构可以被想象成由一个父节点密钥和子密钥,或者说是祖先和后代组成。所有的父密钥都是存储密钥,也就是说这些密钥都是用于加密它们的子密钥的。因此这些存储密钥用于保护它们的子密钥,当子密钥被存储到TPM安全的硬件边界之外时,父密钥能够提供保密性和完整性。这些存储密钥的用处也因此被限制这一种。他们不能用于通用的解密操作,因为这样会泄漏子密钥的私密信息。
在组织架构最顶端的终极父密钥就是主密钥。子密钥可以是存储密钥,这种情况下它可以有自己的子密钥。子密钥也可以是非存储密钥,这种情况下它们是叶子密钥:只能是一个子密钥,而不能有自己的子密钥。
每一个密钥在创建时都会设置自己的属性。密钥属性包括以下部分:
- 用途,比如签名或者加密。
- 类型,对称或者非对称,以及相关的算法。
- 秘钥复制相关的限制。
- 用途相关的限制。
与TPM1.2仅支持RSA不同的是,TPM2.0支持多种非对称加密算法。TPM2.0还引出了一些完全不同的秘钥类型。
一个对称签名秘钥可以被用于TPM的HMAC命令。TPM2.0可以使用一个在TPM外从不以明文存在的秘钥做对称算法签名(又叫做MAC,Message Authentication Code)。
TPM软件规范中包含可以作为通用加解密秘钥的对称秘钥,比如AES。但是,因为一些潜在的出口限制等因素,TPM厂商可能不会包含这些功能。正因为如此,这些相关的命令在PC客户端平台规范中是可选的。从过去的经验来看,TPM厂商并没有实现这些可选的特性。
秘钥复制是指将一个秘钥从一个组织架构下拷贝到另外一个地方(组织架构)。这个秘钥可以成为另外一个父秘钥的子秘钥。目的组织架构或者父秘钥可以在相同或者不同的TPM中。主秘钥不能被复制;它们对于一个TPM的一个组织架构来说是固定的。
秘钥复制的初始作用是秘钥备份。如果一个秘钥被永久地锁定到一个TPM中,但是这个TPM或者TPM所在的主板损坏了,这个秘钥也就永远的丢失了。第二种应用案例就是在多个设备之间共享秘钥。举例来说,一个用户的签名秘钥可以在他的笔记本,平板和手机之前复制。
TPM1.2有一个和秘钥复制类似的过程叫做秘钥迁移。从字面意义上看,迁移的言外之意就是一个秘钥经过迁移之后只存在于目的为止,而原来的位置已经没有这个秘钥了。但是实际上并不是这样的。秘钥迁移完成之后,秘钥仍然存在于原来的位置。基于这个原因,TPM2.0将这个名字修改成了更加准确的复制。
TPM2.0的秘钥有两个控制复制的属性。在极端情况下,一个秘钥可以被锁定到一个TPM的一个父秘钥下,永远不能被复制。相反的极端情况是,一个秘钥可以随意地被复制到相同或者不同TPM的另外一个父秘钥下。
一个在上述两种极端情况中间的应用场景是,一个秘钥可以在其父秘钥被复制时也被默认地复制。这种场景可以实现对一个秘钥树的一阵个秘钥分支进行复制。这样一来,一旦一个父秘钥被复制,它所有的子秘钥也会被复制到目的位置。
TPM规范有讨论复制根和复制组的概念。复制根是指一个可以被复制的秘钥。复制的过程是显式地作用在这个秘钥上的。复制组代表这个根秘钥的所有子秘钥。当根秘钥被复制时,它的复制组也被隐式地复制。虽然子秘钥没有被显式地复制,但是因为它们的父秘钥被复制,所以它们也都跟着被复制。
控制秘钥复制属性的定义如下:
- fixedTPM:如果秘钥的这个属性被设置,这个秘钥就不能被复制了。虽然从字面意义上理解,秘钥应可以被复制到同一个TPM的不同秘钥组织架构下,但是事实上却不是这样的。
- fixedParent:如果秘钥的这个属性被设置,这个秘钥就不能被复制到不同的父秘钥下。这相当于秘钥被锁定到一个父秘钥下。
这两个布尔属性一共有如下四种组合:
- 最简单的一种是fixedTPM为真,fixedParent为假,因为TPM不允许这个配置组合存在。因为fixedTPM已经表明秘钥不能以任何形式被复制,这样fixedParent为false又暗示秘钥可以复制到不同的父秘钥下,这就自相矛盾了。
- fixedTPM和fixedParent都为真,表示这个秘钥不能以显式或隐式的方式被复制。
- fixedTPM为假,fixedParent为真,表示一个秘钥不能显式地被复制。因为它被锁定到一个父秘钥下。但是如果它的父秘钥被复制了,这个秘钥会被隐式地被复制。也就是说这个秘钥可以是复制组的一个,但是不能是复制根。
- fixedTPM和fixedParent都为假,表示一个秘钥可以以复制组或者复制根的方式被复制。如果它是一个父秘钥,那它的子秘钥也将跟它一起被复制。
上述的第四种情况或许是最有意思的,因这个秘钥可以是一个复制根。比如说,它允许复制一组秘钥,在规范中叫做复制组。意思是说,一旦父秘钥被复制了,父秘钥所有的子秘钥也立即被复制到新的位置,不同显式地对每个子秘钥进行复制操作。这也简化了跟踪一个秘钥位置的工作量。你只需要跟踪子秘钥的fixedParent为真的父秘钥就可以。
我们还需要知道的是,这些子秘钥仍然被它原有的父秘钥加密。被显式复制的秘钥必须设置fixedParent为假。经过复制之后,子秘钥可以加载到它们的父秘钥可以加载到的TPM设备中,也不用关心父秘钥由什么位置加载进TPM的。fixedParent决定的是一个秘钥是否可以被直接复制,而不是当它的父秘钥被复制时它能不能被隐式的复制(因为这是一定会发生的)。换句话说,子秘钥不会因为任何其他的TPM操作而被复制。一旦一个秘钥的父秘钥被复制,这个秘钥可以很容易地被复制并加载到新的位置。
一个子秘钥可以有多于一个父秘钥。秘钥复制的过程会建立一个新的父子秘钥关系,但是不会破坏旧的关系。秘钥被复制后它就同时是新旧父秘钥的子秘钥了。一个秘钥也可以存在于多个复制组里,只要这个秘钥有多个父秘钥的fixedParent为假。也就是说一个秘钥可以有多个是复制根的父秘钥。任何一个父秘钥被复制,这个子秘钥都将会被隐式地复制。
TPM给秘钥的父子关系增加了一个限制。只有满足下面两个条件时,一个配置为fixedTPM为真的子秘钥才可以被创建:
- 子秘钥的父秘钥同样是fixedTPM为真(父秘钥不能被显式复制)
- 子秘钥的父秘钥的fixedParent为真(父秘钥不能被隐式地复制)
TPM强制主密钥是这样的属性,这就保证了它固定在一个TPM中的属性。
一个签名密钥的“签名”属性的变种是“限制”属性。受限制的密钥主要用于对TPM的认证数据结构签名。这些结构包括平台配置寄存器(PCR)引用,一个正在被认证的TPM对象,一个针对TPM时间的签名,或者是针对一个审计摘要的签名。我们都知道,签名再操作上当然是针对一个摘要,但是签名验证者想要确保这个摘要不是由外部的伪造数据计算而来,然后发送给TPM来签名。比如说,一个“引用”就是一个针对一组PCR值得签名,但是真正的签名过程实际上是针对这组PCR值的摘要来做的。一个用户可能对任意PCR做摘要,然后使用一个非限制性的密钥来签名这个摘要。之后这个用户还可以声称这个签名值就是一个“引用”。但是,一个可信赖第三方会发现这个密钥不是限制性密钥,所以不会相信这个声明。因此,一个限制性的密钥可以保证这是针对一个由TPM自己产生的摘要的签名。
重申一下,一个限制性的签名密钥只能对TPM自己产生的摘要签名。这个限制性签名密钥也就是TPM1.2中的信息密钥和身份认证密钥,它们都只能对TPM内部生成的数据做签名。对于TPM内部数据而言,这个属性是很容易保证的,因为TPM是在签名操作的时候才会针对内部数据生成摘要。
但是,对于外部数据而言,只要TPM通过TPM2_SequenceComplete或者TPM2_Hash对由外部提供给TPM的数据做哈希,一个限制性密钥也可以做签名,因为TPM限制的是摘要的生成者而非生成摘要的数据来源。但是这里还有一个问题,因为上述的哈希和后面的签名操作是两个独立的命令,也就是说签名操作时需要调用者传递摘要给TPM,那TPM怎么保证调用者传递给它的摘要是在TPM内部生成的呢?
问题的答案就是凭据。当TPM计算摘要的时候,它同时还会产生一个可以证明这个摘要是TPM自己生成的凭据。并且当用户给TPM2_Sign命令传递摘要参数的同时,也必须有对应的凭据作为参数之一。如果没有的话,限制性密钥不会做签名操作的。
但是这样又怎样呢?即使这样也不能限制你要签名的数据啊(这个疑问应该是针对某些必须确保是对TPM内部的数据并且由TPM自己做哈希的应用场景,这与签名的外部数据并不冲突)?你可以对任意外部数据做上述的哈希操作并得到一个凭据。为什么TPM还要关心摘要数据的来源呢?(就是说这个本身看起来没有区分内外数据来源的能力)
这个问题的答案就是一个神奇的4字节值,叫做TPM_GENERATED。每一个认证数据结构——数据结构由TPM内部数据组成——都由这个值开始。对应的情况是,如果TPM是明确地在对外部数据做哈希,只有外部数据不是以这个神奇数据开始时才会产生凭据。
这样一来,一个连锁的结果就是,你可以使用一个限制性签名密钥对任意的外部数据做签名。但是唯有不能签名以TPM_GENERATED开始的数据。这一点就防止你篡改TPM的的认证数据,这些数据都是以这个值开始的。
受限制的解密秘钥实际上是一个存储秘钥。这种秘钥只用于解密特定格式的数据,包括用于验证一个数据结构的完整性校验值。
只有这样的秘钥才可以作为父秘钥去创建和加载子秘钥对象,或者是去激活一个证书。这些操作为解密的结果增加了一些限制。比如说,加载秘钥并不会返回解密的结果(而是一个秘钥的handle)。
一个不受限制的秘钥可以用于通用的解密操作,给它提供相关的加密数据它就会返回解密后的结果(作为一个加解密模块来用)。如果这个秘钥被允许用作存储秘钥,它就可以解密一个子秘钥的私钥并返回给调用者。如果这个秘钥可以被用于隐藏数据,它不需要检查unseal授权就可以返回被隐藏的(sealed)数据。
加载密钥需要向TPM提供一个加密过的密钥和一个已经加载的父密钥。TPM使用父密钥解密子密钥并解密后的密钥存储在易失性的密钥槽中。
上下文管理包含将一个已经加载的密钥的上下文保存到TPM之外和之后将保存的上下文加载到TPM中。当一个密钥被保存时,它会被一个由组织架构秘密信息派生的对称密钥加密,这个对称密钥叫做组织架构证据。当密钥被加载时,TPM使用这个对称密钥来解密。一个上下文被保存的密钥没有父密钥,但是正如前面所描述的,它和一个组织架构相关联。
那上述两种方式应该使用哪一种?又是为什么呢?在TPM1.2中,上下文管理是很重要的,因为子密钥总是被它的RSA父密钥加密。密钥加载过程需要很费时的RSA解密操作。保存上下文的密钥是被一个对称密钥加密的,所以会快不少。在TPM2.0中,子密钥总是被一个对称父密钥加密,即使父密钥身是非对称密钥也是如此。因为所有的存储密钥都有一个对称秘密信息。这样一来,使用父密钥重新加载一个子密钥与加载一个密钥的上下文一样快,所以就省去了上下文保存的过程。
既然这样,那为什么还要用上下文管理来加载密钥呢?实际上使用上下文加载密钥主要应用在父密钥没有被加载的场景中。因为一个密钥可能是一个很长的密钥链中的一个叶子密钥。所以加载这个密钥可能需要很长的时间来加载它所有的父密钥。而一个父密钥又可能要求很不方便的口令授权。更糟糕的是,如果一个父密钥的授权是一个与PCR状态相关的policy,那授权将不可能实现。
具体来讲,假设一个密钥是主密钥以下第四级的一个密钥。首先是第一级的子密钥被加载,加载之后就不再需要它的父密钥了,因此可以将父密钥从TPM密钥缓存中清除。接下来加载下一级的子密钥,然后重复上述的过程四次直到到达最终的叶子(末端)密钥。一旦末端密钥被加载,它所有的祖先密钥就都可以被清除了。但是,如果后续这个末端密钥也被清除之后,需要再次使用这个密钥时就必须重复上述的过程。一种替代的方案就是将末端密钥的上下文保存。然后TPM可以加载上下文而不用关心它所有的祖先密钥。第18章还会详细地介绍这个过程。
除去三个持续性的组织架构之外,TPM还有一个空组织架构。这个组织架构也有它自己独有的种子,组织架构也可以拥有主密钥和子密钥。但是,种子和主密钥都不是持续性的。TPM重启之后就会重新生成一个空组织架构的种子。因此,这个组织架构下的密钥都是临时性的:也就是说在TPM重启时它们将会被删除。
当然,TPM也可以作为一个证书授权机构(CA)。实际上,除去TPM那些特性,比如PCR,授权策略,审计,组织架构等,它的主要价值就是一个密钥存储硬件。用户私有有的签名密钥受硬件保护,并且还有多种授权方式可选,但仍然可以很容易地备份。TPM的这中广泛可用和昂贵的部分提供了比软件密钥更好的密钥保护机制。
一个第三方的证书授权机构也可以为一个TPM密钥签名一个X.509格式的证书。对于解密密钥来说,因为CA通常要求一个密钥所有权的证据,这会使过程变得很复杂。密钥所有权的证据就是说,证书请求者必须向CA提供证据证明他拥有这个私钥。这通常自签名“证书签名请求(CertificateSigningRequest)”来实现。
对于解密密钥来说,TPM还不能简单地对CSR签名,因为解密密钥被限制成只能做解密而不能用于签名。TPM有一个折中的方案(参考第9章的“激活一个证书”),但是这个方案也需要一个非标准的CA。
TPM一个不太明显的功能是它可以认证存储在设备上的数据。TPM提供了几个命令来支持这个功能。
TPM2_Certify可以断言一个有“名称”的对象已经被加载到TPM中。因为名字是一个对象公共区域的密码学表示,TPM就可以向一个依赖方保证这个对象有一个相关的私密部分。名称也包括了密钥的属性,比如密钥是否是受限制的,是否固定在父节点下或者TPM中,以及授权Policy。
应用案例:认证一个TPM引用密钥
一个签名密钥可以用于认证:比如说,引用(签名)一组PCR值。如果能够向依赖方保证这个签名密钥是限制在TPM中的,并且PCR值也是TPM的,那引用将会更加有用。依赖方会首先使用TPM2_Certify命令来获得一个引用密钥的公钥部分的证书。
自然地我们会想到,认证密钥本身也需要一个证书。最终的结果就是,需要一个指向根证书的证书链。第19章解释了怎样配置TPM密钥证书以及这些证书怎样一级一级被验证,直到根证书。
应用案例:创建一个证书链
一个签名密钥(也是一个叶子密钥)在密钥组织架构很深的末端。一个依赖方(可以认为是密钥的使用者)想要确保密钥链中包括主密钥在内的所有密钥都被使当地保护,并且所有相关的加密算法和密钥大小都有足够的强度。依赖方使用TPM2_Certify命令来获取密钥证书链,这个证书链包含对所有密钥公共部分的签名。
TPM2_Certify会签名整个密钥的公共区域,包括密钥的policy。这也引出了其他的应用。
应用案例:确保一个密钥的授权需要一个数字签名
一个依赖方想要确保只有一个受限制的角色才可以使用签名密钥,这个受限制的角色就是用一个有特定授权密钥的签名。它使用TPM2_Certify来认证一个密钥。因为这个命令的签名包含policy,这也就是验证了这个policy包含TPM2_PolicySigned和角色对应的公钥。
在这个案例中,policy不需要有policyRef参数。数字签名是针对“challenge”而非任何签名者相关的额外数据
应用案例:确保一个密钥的授权需要一个生物识别信息
一个依赖方可以验证一个密钥的policy是否包含一个指纹信息授权,指纹信息授权通过TPM2_PolicySigned使用一指纹读卡器的公钥作为输入,同时还包括一个指向特定用户身份的PolicyRef参数。
这个案例是上一个例子的变种。指纹读卡器签名的数据不仅仅是“challenge”,还有一个policyRef。这个数字签名证明了私钥的所有权和正确的用户指纹信息。
TPM2_NV_Certify为NV索引提供类似的功能。它用于证明NV索引中的数据确实来自TPM。参考第11章了解更多NV索引选项的细节。
应用案例:确保NV数据的正确
一个应用可能会使用一个NV索引作为计数器或者作为与签名密钥的policy结合的位域。索引用于触发密钥的使用:比如,当一个计数器到达一个数值或者位域的某一位被设置。应用需要确保NV索引确实已经被更新了,它可以使用TPM2_NV_Certify对数据签名。
应用案例:引用一个NV扩展索引的等价值
如果一个应用正在使用一个混合的索引作为扩展索引来高效地创建一个新的经过授权的PCR,这个PCR受控于应用本身。(这个示例参考第11章)因为显式的quote命令仅仅报告标准的PCR值。所以应用可以使用TPM_NV_Certify对一个饮用的等价值做签名。
使用TPM2_Certify,TPM2_NV_Certify来签名NV索引的policy之后。依赖方就可以在将NV索引加入另外一个policy之前验证NV索引的policy。
TPM密钥有很多嵌套的结构体。为了引用方便,以下是几个数据结构完全展开到基础类型的表示。
以下是一个典型的RSA密钥:
TPM2B_PUBLIC
- size UINT16
- publicArea TPMT_PUBLIC
- type TPMI_ALG_PUBLIC = TPM_ALG_RSA
- nameAlg TPMI_ALG_HASH = TPM_ALG_SHA256
- objectAttributes TPMA_OBJECT
- authPolicy TPM2B_DIGEST
- size UINT16
- buffer BYTE
- parameters TPMU_PUBLIC_PARMS
- rsaDetail TPMS_RSA_PARMS = TPM_ALG_RSA
- symmetric TPMT_SYM_DEF_OBJECT
- For AES example
- Algorithm TPMI_ALG_SYM_OBJECT
- keyBits TPMU_SYM_KEY_BITS->TPMI_AES_KEY_BITS
- mode TPMU_SYM_MODE->TPMI_ALG_SYM_MODE
- details TPMU_SYM_DETAILS
- scheme TPMT_RSA_SCHEME
- scheme TPMI_ALG_RSA_SCHEME = e.g., TPM_ALG_OAEP
- details TPMU_ASYM_SCHEME = e.g., TPMS_SCHEME_OAEP
- keyBits TPMI_RSA_KEY_BITS = e.g. 2048
- exponent UINT32 = default 2^16 + 1
- symmetric TPMT_SYM_DEF_OBJECT
- unique TPMU_PUBLIC_ID->TPM2B_PUBLIC_KEY_RSA
- size UINT16
- buffer BYTE
- rsaDetail TPMS_RSA_PARMS = TPM_ALG_RSA
TPMT_SENSITIVE
- sensitiveType TPMI_ALG_PUBLIC = TPM_ALG_RSA
- authValue TPM2B_AUTH (TPM2B_DIGEST)
- seedValue TPM2B_DIGEST
- sensitive TPMU_SENSITIVE_COMPOSITE,TPM2B_PRIVATE_KEY_RSA
- size UINT16
- buffer BYTE
以下是一个典型的HMAC密钥:
TPM2B_PUBLIC
- size UINT16
- publicArea TPMT_PUBLIC
- type TPMI_ALG_PUBLIC = TPM_ALG_KEYEDHASH
- nameAlg TPMI_ALG_HASH = TPM_ALG_SHA256
- objectAttributes TPMA_OBJECT -> UINT32
- authPolicy TPM2B_DIGEST
- size UINT16
- buffer BYTE
- parameters TPMU_PUBLIC_PARMS
- keyedHashDetail TPMS_KEYEDHASH_PARMS
- scheme TPMT_KEYEDHASH_SCHEME
- scheme TPM_ALG_HMAC
- details TPMU_SCHEME_KEYEDHASH
- hmac TPMS_SCHEME_HMAC
- hashAlg TPMI_ALG_HASH = TPM_ALG_SHA256
- scheme TPM_ALG_HMAC
- scheme TPMT_KEYEDHASH_SCHEME
- keyedHashDetail TPMS_KEYEDHASH_PARMS
- unique TPMU_PUBLIC_ID
- keyedHash TPM2B_DIGEST
*size UINT16
- buffer BYTE
- keyedHash TPM2B_DIGEST
*size UINT16
TPMT_SENSITIVE
- sensitiveType TPMI_ALG_PUBLIC = TPM_ALG_KEYEDHASH
- authValue TPM2B_AUTH
- size UINT16
- buffer BYTE
- seedValue TPM2B_DIGEST
- size UINT16
- buffer BYTE
- sensitive TPMU_SENSITIVE_COMPOSITE
- bits TPM2B_SENSITIVE_DATA
- size UINT16
- buffer BYTE
- bits TPM2B_SENSITIVE_DATA
如下是一个典型的ECC密钥:
TPM2B_PUBLIC
- size UINT16
- publicArea TPMT_PUBLIC
- type TPMI_ALG_PUBLIC = TPM_ALG_ECC
- nameAlg TPMI_ALG_HASH = TPM_ALG_SHA256
- objectAttributes TPMA_OBJECT
- authPolicy TPM2B_DIGEST
- size UINT16
- buffer BYTE
- parameters TPMU_PUBLIC_PARMS
- eccDetail TPMS_ECC_PARMS
- symmetric TPMT_SYM_DEF_OBJECT
- For AES example
- Algorithm TPMI_ALG_SYM_OBJECT = TPM_ALG_AES
- keyBits TPMU_SYM_KEY_BITS->TPMI_AES_KEY_BITS
- mode TPMU_SYM_MODE->TPMI_ALG_SYM_MODE = TPM_ALG_CBC
- details TPMU_SYM_DETAILS
- scheme TPMT_ECC_SCHEME
- scheme TPMI_ALG_ECC_SCHEME = TPM_ALG_ECDSA
- details TPMU_SIG_SCHEME
- ecdsa TPMS_SCHEME_ECDSA TPMS_SCHEME_SIGHASH
- hashAlg TPMI_ALG_HASH = TPM_ALG_SHA256
- ecdsa TPMS_SCHEME_ECDSA TPMS_SCHEME_SIGHASH
- curveID TPMI_ECC_CURVE = TPM_ECC_NIST_P256
- symmetric TPMT_SYM_DEF_OBJECT
- kdf TPMT_KDF_SCHEME
- scheme TPMI_ALG_KDF = TPM_ALG_NULL
- details TPMU_KDF_SCHEME
- eccDetail TPMS_ECC_PARMS
- unique TPMU_PUBLIC_ID
- ecc TPMS_ECC_POINT
- x TPM2B_ECC_PARAMETER
- size UINT16
- buffer BYTE
- y TPM2B_ECC_PARAMETER
- size UINT16
- buffer BYTE
- x TPM2B_ECC_PARAMETER
- ecc TPMS_ECC_POINT
TPMT_SENSITIVE
- sensitiveType TPMI_ALG_PUBLIC = TPM_ALG_ECC
- authValue TPM2B_AUTH TPM2B_DIGEST
- Size UINT16
- Buffer BYTE
- seedValue TPM2B_DIGEST
- size UINT16
- buffer BYTE
- sensitive TPMU_SENSITIVE_COMPOSITE
- ecc TPM2B_ECC_PARAMETER
- size UINT16
- buffer BYTE
- ecc TPM2B_ECC_PARAMETER
TPM最初是用于硬件安全模块(HSM)安全地存储密钥。TPM可以将密钥存储在四个组织架构中的一个。每一个组织架构都有一个主要的父密钥和一些树型组织的子密钥。父密钥是都是加密密钥,子密钥在离开TPM之前都会被父密钥加密。
密钥可以被复制(用另外一个密钥加密(当作父密钥)),如果秘密要被复制了,父密钥的所有子密钥也都将被复制。密钥复制必须遵守一些限制。一些密钥是被固定在某一个TPM中的,它们是不能被复制的。还有一些密钥与它们的父密钥绑定,所以只有当它们的父密钥被复制时它们才可以隐式地复制。
密钥的用途也可以被限制。密钥可以被配置成只能签名或者只能解密密钥,他们还可以被限制只能签名或者解密特定的数据。最后,密钥可以被其他的TPM密钥认证,一个依赖方可以验证公钥,密钥的属性,甚至是密钥的policy。