Go实施静态数据加密
介绍
加密网络流量已成为默认设置。有诸如SSH和TLS之类的标准化协议,以及诸如“让我们加密”之类的项目可以保护通过网络发送的数据。例如,TLS接收数据流,将该流分块为消息,并在通过网络发送消息之前对每个消息进行加密。TLS确保对每条消息进行加密和认证,以防止攻击者读取或修改通过TLS连接发送的任何数据。
令人惊讶的是,在文件加密方面,或更确切地说在静态数据加密方面,没有可比的标准或解决方案。每当将数据安全地存储在不受信任的存储中时,您或多或少都将自己。结果,发明了许多项目和产品来提供某种文件或数据加密。不幸的是,这些解决方案是孤岛,默认情况下是不兼容问题。甚至更糟糕的文件加密也经常出错。作为一个示例,GPG仅显示在Adam Langley的博客文章中。
DARE格式
在Minio,我们希望为用户提供存储加密数据的功能。但是,我们希望为静态数据加密提供一种通用的解决方案,而不是发明其他任何人都无法使用的孤岛解决方案。因此,我们提出了一种用于以安全方式加密任意数据流的格式。格式应为:
提供加密数据的机密性和完整性。
适合许多用例-不仅解决我们的特定问题。
尽可能简单。
保持较小的开销。
提供加密数据的机密性和完整性是最重要的属性。我们以一种非常简单的方式组合了所有原语,以保持较小的攻击面和较低的实现复杂性。因此,数据流被分成一系列的包,每个包分别进行加密。加密的方式是,在不破坏使用的密码的情况下,不可能读取或修改加密的数据包。加密的软件包如下所示:
header (16 bytes) | payload (1 byte - 64 KB) | tag (16 bytes)
标头包含软件包的一些元数据,例如版本,密码套件,有效载荷长度,序列号和随机数。标签是在对有效负载进行加密时计算出的校验和,并且取决于标头和有效负载的每个字节。16字节的标头包含以下字段:
version | cipher | payload length | sequence number | nonce 1 byte 1 byte 2 bytes 4 bytes 8 bytes
每个程序包均使用AEAD密码进行加密 — AES-256-GCM或ChaCha20Poly1305。顺序号基本上是在每个加密包之后递增的计数器,以防止对包重新排序。随机数的目的是减少密钥重用的损害。如果曾经重复使用加密密钥(并且不会出现随机数),则攻击者将能够对具有相同序列号的软件包的两个有效载荷进行XOR,从而对两个纯文本进行XOR。随机数是随机生成的,可以缓解由意外密钥重用引起的此类攻击。我们不会在每个程序包中生成一个随机数,而是在加密过程开始时生成一次随机数,并在每个程序包头中重复一次。原因是每个程序包生成单独的随机数没有优势。
DARE格式结合了经过身份验证的密码方案和非常简单的重排序保护机制,以实现防篡改特性。但是,一旦两次使用加密密钥,该防篡改属性就会丢失。因此,DARE需要唯一的加密密钥。在DARE规范中,我们对如何正确执行此操作提供了一些建议。
Minio(作为对象存储)处理大量数据。因此,DARE旨在处理大型数据流,同时保持较低的开销。每个加密的程序包都包含一个标头和一个身份验证标签,可将加密数据流的大小增加约0.05%。为了说明这一点:5 GB数据流的开销约为2.5 MB。此外,DARE支持高达256 TB的大型数据流,对于任何当前用例而言,这应该足够了。但是,如果将来必须对较大的流进行加密,则可以通过进行不同的折衷来调整DARE的较新版本以支持较大的流。
安全IO
Minio还提供了DARE格式的参考Go实现。它导出易于使用的API,以安全地加密/解密任意数据流。下面的示例演示如何使用32字节的主密钥加密文件。
masterkey := []byte("my-secret-32-byte-master-enc-key")
// this nonce must be unique (should generated randomly)
// It must be remembered but needn't be secret.
nonce := []byte{
0, 1, 2, 3, 4, 5 ,6 ,7, 8, 9, 10, 11, 12, 13, 14, 15,
}
plaintext, err := os.Open("my-unencrypted-file")
if err != nil {
fmt.Printf("Failed open 'my-unencrypted-file': %v", err)
return
}
defer plaintext.Close()
ciphertext, err := os.Create("my-encrypted-file")
if err != nil {
fmt.Printf("Failed create 'my-encrypted-file': %v", err)
return
}
defer ciphertext.Close()
// derive an unique encryption key
kdf := hmac.New(sha256.New, masterkey)
kdf.Write(nonce)
config := sio.Config {
Key: kdf.Sum(nil),
CipherSuites: []byte{sio.AES_256_GCM},
}
_, err = sio.Encrypt(ciphertext, plaintext, config)
if err != nil {
fmt.Printf("Failed to encrypt data: %v", err)
return
}可以在godoc.org上找到其他示例和软件包文档。此外,如果您想使用DARE格式并在计算机上加密/解密数据流,则可以签出此演示工具。
结论
Minio将使用DARE进行服务器端和客户端加密。这将使我们的用户能够使用客户端加密来加密他们的数据,并使用服务器端加密来解密数据,反之亦然。我们希望DARE不仅对我们的用户而且对广大的开发人员社区都是有用的解决方案。如果您有任何疑问或建议,请加入我们的松弛频道。随时欢迎反馈