密钥环¶
将所有配置文件明文保存在客户端上并非总是一个好主意,特别是存在多个用户使用同一设备的同一客户端来访问服务器时。
为了避免其他接触设备的用户利用客户端暴露在外的文件来对机要信息进行推断,服务端允许用户在服务器上托管一条或多条密钥,它们可用于加密客户端本地的文件,例如载有用户下载记录和收藏的文档与目录的偏好文件。这一过程通常是由客户端自动完成而对用户透明的。
我们称呼服务端托管密钥的功能为密钥环(Keyrings)。基于用途,一般称呼被托管的密钥为数据密钥(Data Encryption Key, DEK)。
在一个用户拥有的众多密钥中,服务端允许择选其中一条作为偏好数据密钥,该密钥将在用户登录成功时包含在请求响应中。该密钥通常被客户端用来加解密本地的用户偏好文件。客户端也可通过常规方法在其他任何时候重新获取它。
密钥的托管¶
为了确保用户密钥在上传后即使意外泄漏也不会被轻易窃取,数据密钥不能以明文状态上传到服务器,而应首先使用一个派生密钥进行加密。为了确保仅有用户能够解密已加密的密钥,用于加密的密钥通常自用户密码经密钥派生算法派生而来。
除了用户密码,生成派生密钥还需要一个随机生成的盐值(salt)。在得到派生出的密钥加密密钥(Key Encryption Key, KEK)后,一种可行的做法是,利用 AES-GCM 来加密数据密钥——该加密算法接受用于加密的密钥、随机数和需被加密的明文,输出密文和消息认证标签。
服务端不限制密钥环中内容的格式,尽管其限制单条内容的最大长度为 512 字节。因此,可以将密文、消息认证标签及加密过程中用到的全部参数组装为一个 JSON 对象,在组装过程中可以利用 Base64 算法编码。客户端可以凭借正确的用户密码和记录的各种参数,准确地将密文还原为明文密钥。
如果数据密钥的加密与用户密码强相关,为了避免被加密的密钥在用户修改密码后无法正确解密,需要在修改密码前以新密码对数据密钥进行重新加密,并以重新加密后的密文替代旧密文。这一过程通常由客户端自动完成。