Xeo*_*oss 14 encryption aes go large-data
我有一些大型文件,我想在通过线路发送或保存到磁盘之前进行AES加密.虽然似乎可以加密流,但似乎有警告不要这样做,而是人们建议将文件分成块并使用GCM或crypto/nacl/secretbox.
由于真实性要求,处理数据流更加困难.我们不能加密 - 然后MAC:根据它的性质,我们通常不知道流的大小.我们无法在流完成后发送MAC,因为通常由关闭的流指示.我们无法动态解密流,因为我们必须查看整个密文才能检查MAC.尝试保护流会增加问题的复杂性,没有好的答案.解决方案是将流分解为离散的块,并将它们视为消息.
文件被分段为4KiB块.每次修改时,每个块都会获得一个新的随机128位IV.128位身份验证标记(GHASH)可保护每个块免受修改.
如果解密了大量数据,则在验证身份验证标记之前,并不总是可以缓冲所有解密数据.将数据拆分成小块可以解决延迟身份验证检查的问题,但会引入一个新问题.块可以重新排序......因为每个块都是单独加密的.因此,必须以某种方式将块的顺序编码到块本身中,以便能够检测重新排列任意数量的块.
任何具有实际密码学经验的人都可以指出我正确的方向吗?
我在问这个问题之后意识到,简单地说不能将整个字节流放入内存(加密10GB文件)之间存在差异,字节流也是一个未知的长度,可能会长时间超过流的启动需求要解码(24小时直播视频流).
我最感兴趣的是大blob,在开始需要解码之前可以到达流的末尾.换句话说,加密不需要将整个明文/密文同时加载到存储器中.
正如您已经从研究中发现的那样,对于大文件的身份验证加密没有太多优雅的解决方案.
传统上有两种方法可以解决这个问题:
将文件拆分为块,单独加密每个块,让每个块都有自己的身份验证标记.AES-GCM将是最好的模式.此方法导致文件大小膨胀与文件大小成比例.您还需要为每个块提供唯一的随机数.您还需要一种方法来指示块开始/结束的位置.
使用带缓冲区的AES-CTR加密,Hash.Write为每个加密数据缓冲区调用HMAC.这样做的好处是加密可以一次完成.缺点是解密需要一次通过验证HMAC,然后另一次通过实际解密.这里的优点是文件大小保持不变,加上IV和HMAC结果的大约约48个字节.
两者都不理想,但对于非常大的文件(~2GB或更多),第二种选择可能是首选.
我使用下面的第二种方法在Go中包含了一个加密示例.在这种情况下,最后48个字节是IV(16个字节)和HMAC的结果(32个字节).注意IV的HMAC也.
const BUFFER_SIZE int = 4096
const IV_SIZE int = 16
func encrypt(filePathIn, filePathOut string, keyAes, keyHmac []byte) error {
inFile, err := os.Open(filePathIn)
if err != nil { return err }
defer inFile.Close()
outFile, err := os.Create(filePathOut)
if err != nil { return err }
defer outFile.Close()
iv := make([]byte, IV_SIZE)
_, err = rand.Read(iv)
if err != nil { return err }
aes, err := aes.NewCipher(keyAes)
if err != nil { return err }
ctr := cipher.NewCTR(aes, iv)
hmac := hmac.New(sha256.New, keyHmac)
buf := make([]byte, BUFFER_SIZE)
for {
n, err := inFile.Read(buf)
if err != nil && err != io.EOF { return err }
outBuf := make([]byte, n)
ctr.XORKeyStream(outBuf, buf[:n])
hmac.Write(outBuf)
outFile.Write(outBuf)
if err == io.EOF { break }
}
outFile.Write(iv)
hmac.Write(iv)
outFile.Write(hmac.Sum(nil))
return nil
}
Run Code Online (Sandbox Code Playgroud)
加密后使用 HMAC 是一种有效的方法。但是,HMAC 可能会很慢,尤其是在使用 SHA-2 的情况下。您实际上可以对 GMAC(GCM 的底层 MAC)执行相同的操作。找到一个实现可能很棘手,但 GMAC 是在密文之上,所以如果你真的想要,你可以简单地单独执行它。还有其他方法,例如用于 TLS 1.2 和 1.3 的带有 AES 的 Poly1305。
对于 GCM(或 CCM 或 EAX 或任何其他经过身份验证的密码),您需要验证块的顺序。您可以通过创建一个单独的文件加密密钥,然后使用 nonce 输入(12 字节 IV)来指示块的编号来做到这一点。这将解决IV的存储并确保块是有序的。您可以使用 KDF(如果您有一种独特的方式来指示文件)或通过用主密钥包装随机密钥来生成文件加密密钥。