使GCM / CBC密码在golang中流畅

时间:2016-09-07 19:56:07

标签: encryption go aes

Go中的GCM和CBC AES密码不能与StreamWriter或StreamReader一起使用,这迫使我将整个文件分配到内存中。显然,对于大文件来说这并不理想。

我正在考虑通过将一些固定大小的块分配到内存中,然后将它们提供给GCM或CBC来使它们可流化,但我认为这可能是一个坏主意,因为它们必须有一个原因。 #39;这样设计。

有人可以解释为什么在不将整个文件分配到内存中的情况下无法使用这些操作模式吗?

2 个答案:

答案 0 :(得分:3)

简单回答 - 他们是如何设计API的。

CBC和GCM是非常不同的模式。 GCM是AEAD模式(带有关联数据的经过身份验证的加密)。你真的需要身份验证吗?如果没有,对于大文件CBC是一个很好的选择。你也可以使用CTR或OFB,但他们是流媒体模式,对选择IV非常严格。

在实施任何内容之前,我建议你阅读这些模式。你必须至少了解他们需要哪些参数,目的以及如何生成它们。

CBC

BlockMode接口非常适合加密大文件。您只需要逐块加密。 CBC需要填充,但Go没有实现。至少,我没有看到一个。你将不得不以某种方式处理它。

GCM

GCM使用AEAD接口,它只允许您加密和解密整个消息。绝对没有理由为什么要这样实施。 GCM是一种流模式,它实际上非常适合流加密。唯一的问题是身份验证。 GCM在末尾生成一个标签,其作用类似于MAC。要使用该标记,您无法加密无穷无尽的数据流。您必须将其拆分为块并单独进行身份验证。或者做其他事情,但在某些时候你必须阅读该标签并进行验证,否则使用GCM毫无意义。

一些库所做的事情,包括Go,它们会在加密时隐含地附加该标记,并在解密时读取并验证它。就个人而言,我认为这是一个非常糟糕的设计。标签应该作为一个单独的实体提供,你不能假设它总是在最后。而这并不是Go实施中唯一的问题。 对不起那个咆哮。最近,我处理了一个特别糟糕的实施。

作为一种解决方案,我建议您将流分成块并使用唯一的随机数(这非常重要)单独加密它们。每个块最后都有一个标签,您应该验证。这样您就可以使用GCM身份验证。是的,它有点难看,但Go并没有让你访问内部方法,所以你可以自己创建加密API。

作为替代方案,您可以找到不同的实现。甚至可能是一个C库。我可以建议mbedtls。对我而言,它是我在API方面遇到的最佳实现。

答案 1 :(得分:0)

这里有一个流实现,用于从BlockMode中读取。您的里程可能会有所不同。

type BlockReader struct {
    buf   []byte
    block cipher.BlockMode
    in    io.Reader
}

func NewBlockReader(blockMode cipher.BlockMode,reader io.Reader) *BlockReader {
    return &BlockReader{
        block: blockMode,
        in:    reader,
    }
}

func (b *BlockReader) Read(p []byte) (n int, err error) {
    toRead := len(p)
    mul := toRead/b.block.BlockSize()
    size := mul*b.block.BlockSize()
    if cap(b.buf) != size{
        b.buf = make([]byte,toRead,toRead)
    }

    read, err := b.in.Read(b.buf)
    if err != nil {
        return 0,err
    }

    if read < b.block.BlockSize(){
        return 0,io.ErrUnexpectedEOF
    }
    b.block.CryptBlocks(b.buf,b.buf)
    return copy(p,b.buf),nil
}