我有一些大文件,我想在通过网络发送或保存到磁盘之前进行AES加密.虽然看起来可能是encrypt streams,但似乎是warningsdoing this,相反,人们建议将文件分成块,并使用GCM或crypto/nacl/secubox.

由于真实性要求,处理数据流更加困难.我们不能加密然后MAC:从本质上讲,我们通常不知道流的大小.我们不能在流完成后发送MAC,因为这通常由流被关闭来指示.我们不能解密飞翔上的流,因为我们必须看到整个密文才能判断媒体访问控制.试图保护流会给问题增加极大的复杂性,而且没有好的答案.解决方案是将流拆分成离散的块,并将其视为消息.

文件被分割成4KB的块.每次修改时,每个块都会获得一个新的随机128位IV.128位身份验证标签(GHASH)保护每个块不受修改.

如果大量数据被解密,则直到验证标签被验证之前,并不总是可能缓冲所有解密的数据.将数据拆分成小块修复了延迟身份验证判断的问题,但引入了一个新的问题.这些大块可以重新排序....因 for each 区块都是单独加密的.因此,必须以某种方式将块的顺序编码到块本身中,以便能够检测到重新排列任意数量的块.

有实际密码学经验的人能给我指个正确的方向吗?

Update

在问了这个问题后,我意识到,无法将整个字节流放入内存(加密10GB文件)和字节流also是未知长度之间存在差异,字节流also可能会持续很久,而不再需要对其进行解码(24小时直播视频流).

我最感兴趣的是大的BLOB,在开始需要解码之前可以到达流的末尾.换句话说,encryption that does not require the whole plaintext/ciphertext to be loaded into memory at the same time.

推荐答案

正如您已经从您的研究中发现的那样,对于大文件的authenticated加密来说,没有多少优雅的解决方案.

传统上有两种方法来解决这个问题:

  • 将文件拆分为块,分别加密每个块,并让每个块都有自己的身份验证标签.AES-GCM将是实现这一点的最佳模式.此方法会导致文件大小与文件大小成比例inflating .对于每个块,还需要一个唯一的nonce.您还需要一种方法来指示块的开始/结束位置.

  • 使用带缓冲区的AES-CTR进行加密,在HMAC上 for each 加密数据缓冲区调用Hash.Write.这样做的好处是加密可以在一次通过中完成.缺点是解密需要一次通过验证HMAC,然后需要另一次通过才能实际解密.这里的优点是文件大小保持不变,加上IV和HMAC结果的大约48个字节.

这两种方法都不理想,但是对于非常大的文件(大约2 GB或更大),第二种可能是首选的.

我使用下面的第二种方法在Go中提供了一个加密示例.在这个场景中,最后48个字节是IV(16个字节)和HMAC的结果(32个字节).还要注意静脉注射的速度.

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
}

Go相关问答推荐

Go - os.userHomeDir()在WSL上返回C:\Users\

Go协议缓冲区导入问题

如何给杜松子wine 的路由加上一个名字,比如Laravel ?

如何在 zap 记录器库中使用自定义时间函数

使用 Golang 获取目录磁盘使用情况

转到 bufio.Writer、gzip.Writer 并上传到内存中的 AWS S3

使用 httptest 对 http 请求进行单元测试重试

Secrets Manager Update Secret - Secret String 额外的 JSON 编码

正确的 shell 程序进入 golang alpine docker 容器的入口点?

io.Reader 无限循环与 fmt.Fscan

Opensearch 错误 ping 弹性服务器:由未知权威签署的 x509 证书

Go 中的 HTTP 请求验证中间件

在 Go 中发送 ack 和 term 后消息仍在 nats 限制队列中

当有多个同名包时如何在vscode中显示golang包完整路径?

如何从 tinygo webassembly 目标返回对象

实现接口的指针的泛型类型是什么?

如何在 Go 中使用 Pact 返回错误请求(400、500)?

防止在 Go 公用文件夹中列出目录

如何在 Gorm 中获得特定日期的最大值?

Go:如何通过 GIN-Router 从 AWS S3 将文件作为二进制流发送到浏览器?