虚拟化加密磁盘密钥设置方案浅析
- 前言
- 密钥设置方案
- 密钥管理服务-KMS
- 密钥设置方案-TKS1
- 两级加密设计
- 弱熵密码派生
- 密钥切分存储
前言
- 虚拟化组件可以使用多种加密算法对虚拟机磁盘的原始内容进行加解密,比如AES、RSA、SM2/SM3/SM4等,用户写入的数据经过加密后落盘,读取数据时从盘中读取数据后解密返回,这个过程中,用于数据加解密的密钥一旦被hacker破解,磁盘数据就会暴露,因此密钥不能对外公开,其安全性至关重要。
- 通常密钥的管理会交给一个可信的硬件,比如加密机,第三方厂商基于加密机的能力开发一套软件接口,供有需求的用户存取密钥,比如阿里云密钥服务;用户使用密钥的场景各种各样,在虚拟化场景下,使用密钥对磁盘加密是其中之一,密钥对加密磁盘至关重要,密钥损毁或者丢失意味着用户的数据不可访问,因此从可信的服务中获取密钥后(或者由加密磁盘的用户输入),如何使用、保存、读取和销毁,需要小心处理,这中间的任何环节出错都可能导致密钥损毁。在加密磁盘软件开发初始阶段,密钥的上述操作的正确性都通过软件开发者来保证,但越来越多的实践证明,随着加密软件的升级迭代,因为操作不当导致的密钥丢失及对应磁盘数据的丢失也越来越多。因此有必要将密钥的设置流程统一起来并形成规范,规范中定义了密钥操作的正确规程并保证规范版本的兼容性,而加密软件开发者在使用密钥时只要遵循该规范,就可以避免密钥丢失磁盘数据丢失的情况。加密磁盘的密钥设置方案的经典论文就是TKS1(Two level Key setup Scheme),基于此论文而形成的加密磁盘格式规范LUKS(Linux Unified Key Setup) On-Disk Format Specification则定义了虚拟化场景下加密磁盘的头部标准格式。
- 开源密钥设置方案TKS1(An anti-forensic, two level, and iterated key setup scheme)的整体流程如下图所示,我们后续会围绕该图详细分析其每个模块的设计初衷和作用。
密钥设置方案
- 在介绍开源的密钥设置方案细节之前,作为对比,我们首先简单介绍第三方密钥管理服务,这里我们以阿里云的密钥管理服务为例。
密钥管理服务-KMS
- 用户需要加密数据时,其流程如下:
- 用户应用程序调用GenerateDataKey接口,向KMS传入如下信息用于生成数据密钥。
KeyId:创建的密钥的KeyId或别名。
NumberOfBytes:数据密钥的长度。从安全角度考虑,推荐设置为24字节及以上长度。 - 用户应用程序收到KMS返回的数据密钥,其中包含数据密钥明文、数据密钥密文、加密参数(包含加密算法以及加密后的初始向量IV)。
数据密钥密文是由GenerateDataKey接口中传入的密钥加密数据密钥明文生成的。 - 应用程序使用数据密钥明文在本地对数据进行加密得到数据密文。
- 应用程序将数据密文、数据密钥密文、KMS的密钥(即用于生成数据密钥的密钥)和加密参数(包含生成数据密钥时的加密算法以及IV)进行保存。不保存数据密钥明文。
- 应用程序解密数据流程如下:
- 应用程序读取数据密文、数据密钥密文、KMS的密钥(即加密数据密钥的密钥)和加密参数(包含生成数据密钥时的加密算法以及IV)。
- 应用程序调用KMS的Decrypt接口并传入上一步读取的信息,用于解密数据密钥密文。
- 应用程序收到KMS返回的数据密钥明文。
- 应用程序使用数据密钥明文在本地对数据密文进行解密,得到数据明文。
- 分析上面的流程我们可以看到,加密用的数据密钥明文在数据加密后就立即被销毁,只有密钥的密文和数据密文一起写入到磁盘中,应用程序认为密钥管理服务是可信的,不可能被hacker攻破,因此将管理数据密钥明文的任务交给密钥管理服务,应用程序只需要在加解密时请求密钥明文即可,用完即毁。
密钥设置方案-TKS1
- 上述的密钥管理服务仅提供密钥的存取,拿到密钥后如何正确的操作保证密钥和磁盘数据不丢失,这就是TKS1(An anti-forensic, two level, and iterated key setup scheme)密钥管理方案关注的点。同时TKS1还会关注当密钥并非从可信的服务方获取,而是用户配置时,怎样处理用户配置的弱熵密码。
- TKS1的目标是设计安全的本地密钥设置软件方案,这个软件方案中,允许用户(或者应用程序)提供一个密码作为访问该软件的钥匙,我们知道用户提供的密码通常为弱熵字符串,如果直接将其作为加密磁盘的密钥,很容易被暴力破解,因此关于密钥管理系统,需要满足两个安全需求,首先允许用户通过输入密码获得磁盘密钥的读写权限;其次不能将用户密码直接用做磁盘加密的密钥。
- 本地密钥设置软件将加密磁盘的密钥密文作为元数据,与磁盘数据一并放到磁盘中,当用户不再使用该磁盘想要删除磁盘数据和其上的密钥时,由于磁盘块的擦除操作有一定机率失败,因此需要最大程度确保磁盘上的密钥数据被删除。
- 综合上面分析,一个安全的本地密钥设置软件至少需要做到以下三点:
- 允许用户输入密码获得加密磁盘密钥的读写权限
- 用户密码通常为弱熵字符串,不能直接用做加密磁盘的密钥
- 用户删除磁盘上的密钥数据时,需要最大程度确保可靠删除
两级加密设计
- 当磁盘加密要求用户输入密码时,如果用户密码被破解时,则需要更新加解密磁盘的密码。对于新的磁盘,通过新密码进行加密,但对于已经通过原始密码加密的磁盘,如果将其数据解密,再通过新密码进行加密的话,计算开销大且复杂,不利于工程化。这个现象的原因是磁盘的加解密强依赖用户密码,因此我们需要将用户密码与加密磁盘用的密钥解耦,当用户密码被攻破但加解密磁盘的密钥未被攻破时,可以只更新用户密码。另外,这也是不能将用户密码直接用作磁盘加密的第二个原因,第一个原因是前一节提到的用户密码为弱熵字符串容易被破解。
- 为达到用户密码与磁盘密钥解耦的目的,TKS1磁盘密钥管理方案设计为两级加密,其核心思想是引入master key作为加密磁盘的密钥,用于磁盘数据的加密,加密完成后,将master key作为数据,也进行一次加密生成master key的密文,将master key的密文和磁盘数据一并写入磁盘。这里加密master key使用的密钥会强依赖用户密码。注意,master key的明文只存在于内存中,落盘时存放的是master key的密文。两级加密示意图如下:
- TKS1两级加密的具体流程是:
- 用户加密磁盘时,随机生成加密用的密钥 master key,数据加密完成后,对加密用的master key也进行一次加密,将密钥密文和数据密文一并写入磁盘,如下图所示:
上图的流程借用了前面章节中阿里云密钥服务中加密流程的图例,本地密钥管理系统的加密流程有两点区别,一是数据密钥明文是本地生成的;二是数据密钥的密文是基于用户密码派生的密钥加密生成的。如图中增加的注释所述。 - 用户解密磁盘数据时,将磁盘中的数据密文和数据master key的密文一并读出,再基于用户密码派生的密钥解密master key的密文,得到master key的明文,然后对数据进行解密,如下图所示:
- 当用户密码被破解后(但master key未被破解),hacker读取磁盘上的master key密文并解密得到master key的明文,进一步可以使用master key解密磁盘数据获取磁盘内容。这时,我们更新用户密码,派生出新的密钥,对同样的master key进行加密后得到新的master key密文,将原来存放到磁盘上的旧master key密文替换为新的master key密文,可以达到再次保护磁盘数据效果。因为当hacker使用旧的用户密码派生的密钥解密更新后的master key密文时,这时master key的密文由于是新密码加密的,因此用户使用的旧密码解密不会成功,得到的master key数据是错误的。
弱熵密码派生
- 用户设置的密码如果直接用于密钥的加密,因为其弱熵的属性,易被hacker攻破,因此不能直接用于加密master key。TKS1设计了PBKDF2(password based key derive function 2)算法用于生成加密master key的密钥。PBKDF2主要目标是解决两个密码学问题,一是防止用户穷举固定长度(比如12位)的密码并预先通过PBKDF2计算所有密钥并保存到字典,在破解时直接读取字典条目作为可能的密钥进行验证(字典攻击)。二是防止用户通过不断尝试密码暴力破解;
- PBKDF2处理用户密码的示意图如下:
- 算法随机生成固定长度的随机数(salt),与用户设置的密码明文一起(通常添加到明文尾部),作为PBKDF2函数的输入。这样,函数的输入由两部分构成,passphrase和salt,salt的作用是增加PBKDF2的输入位数,如果hacker穷举增加位数后的密码并做hash计算,保存hash值到内存作为字典以供查询,那么salt长度越长,字典需要占用的内存就越大。因此只要salt长度足够,就可以将字典占用的增大到现有硬件无法满足的程度。另外,salt的作用仅仅是增大PBKDF2的输入,不需要保密,因此可以明文保存,没有加密。总结,PBKDF2通过将salt和密码明文一起作为PBKDF2的输入,增加PBKDF2的输入位数以防止字典攻击。
- 算法将PBKDF2设计为多次的hash迭代,增加cpu计算的时间开销,迭代次数越多cpu计算的时间开销越大;且每一次hash计算的输入是前一次hash计算的输出,避免出现重复hash计算可能出现的输出值相同的情况,防止算法退化。通过这样的方式,只要PBKDF2的迭代次数足够多,就可以将暴露破解理论需要的时间开销增大到现有硬件无法达到的程度(比如100年)。总结,PBKDF2通过增加hash计算次数,将暴力破解理论上需要的cpu计算时间增加到不可接受,以此防止暴力破解。PBKDF2的hash阶段伪代码如下,其中+表示异或操作xor:
- 总结上面的描述,TKS1的PBKDF2伪代码如下:
- 输入
- password: 用户密码,用户设置。仅在内存中存在,首次加密时由用户或者第三方密钥管理服务(KMS)提供,后续加解密时从可信的软件或者硬件中获取。
- salt: 盐值,与password一起作为PBKDF2首次迭代的输入。在初始化时随机生成,后续作 为元数据与磁盘密文一起落盘保存。
- iteration-count: hash迭代次数,用户设置。后续作为元数据一起落盘保存。
- derived-key-length: 输出密钥的长度。
- 输出
- result: 加密master key用的密钥,后面以cipher指代。读取加密磁盘数据时,首先从磁盘中取出master key的密文,使用cipher进行解密得到master key的明文,之后用master key明文解密磁盘数据。加密磁盘数据写入时,通过master key明文加密磁盘数据,再使用cipher对master key加密得到密文,作为元数据一并写入磁盘。
密钥切分存储
- TKS1除了解决上述密码学问题外,针对磁盘数据擦除不可靠问题,还设计了master key反取证切分算法。当要擦除的磁盘上存放有master key密文时,保证对master key信息最大概率的有效擦除。反取证切分的核心思想是将数据(这里主要指加密后的master key)的有效信息拆分为多个子块,均匀存放磁盘,一个最简单的想法就是对原始数据进行异或操作,编码得到一组新数据。原始数据可以通过新数据的异或操作恢复。
- 下面我们举例说明反取证算法,例子中,我们分别介绍对原始数据0b1111进行反取证拆分与合并。拆分的大小(blocknumbers)是3,长度(blocksize)是4bit。
- 数据拆分
数据拆分的目标是将原始的1份数据分为3份数据,首先我们随机生成前两份数据,s1:0b0010,s2:0b1011,计算s1异或s2的值I = s1 xor s2=0b1001。最后计算s3的值s2=I xor src=0b0110。通过简单的异或操作,我们将原有的一组4bit数据src用三组4bit数据dst表示,并将计算出的三组拆分数组存放到磁盘。 - 数据合并
当我们需要从磁盘上读取数据时,首先读取拆分后的三组数据,s1,s2,s2,然后计算三组数据的异或结果得到原始数据,dst = s1 xor s2 xor s3。
- 从上面描述的例子中我们可以看到,原始的4bit数据信息被切分为三组存放,如果其中任意一组数据丢失,原始数据都无法恢复。TSK1的反取证切分算法基于以上的思路,引入了扩散因子H,将切分后的数据互相依赖性进一步增强,扩散因子通常就是是1个加密的hash算法。整个master key密文的切分计算的示意图如下: