从密码aes 256 gcm golang中提取标记

nbysray5  于 2021-09-29  发布在  Java
关注(0)|答案(2)|浏览(521)

我用ruby进行加密和解密,并尝试用go重写。我一步一步地尝试,所以从ruby中的加密开始,然后尝试go中的解密,这很有效。但当我尝试用go编写加密,用ruby解密时。我在尝试提取标记时遇到了问题,我解释了需要提取auth标记的原因
ruby中的加密

plaintext = "Foo bar"
cipher = OpenSSL::Cipher.new('aes-256-gcm')
cipher.encrypt
cipher.iv = iv # string 12 len
cipher.key = key # string 32 len
cipher.auth_data = # string 12 len
cipherText = cipher.update(JSON.generate({value: plaintext})) + cipher.final
authTag = cipher.auth_tag
hexString = (iv + cipherText + authTag).unpack('H*').first

我尝试连接一个初始向量、一个密文和身份验证标记,所以在解密之前我可以提取它们,特别是身份验证标记,因为我需要在ruby中调用cipher#final之前设置它们
认证标签
必须在调用cipher#decrypt、cipher#key=和cipher#iv=之后,但在调用cipher#final之前设置标记。执行所有解密后,将在对cipher#final的调用中自动验证标记
这是golang中的函数加密

ciphertext := aesgcm.Seal(nil, []byte(iv), []byte(plaintext), []byte(authData))
src := iv + string(ciphertext) // + try to add authentication tag here
fmt.Printf(hex.EncodeToString([]byte(src)))

如何提取身份验证标签并将其与iv和密文连接起来,以便在ruby中使用解密函数进行解密

raw_data = [hexString].pack('H*')
cipher_text = raw_data.slice(12, raw_data.length - 28)
auth_tag = raw_data.slice(raw_data.length - 16, 16)

cipher = OpenSSL::Cipher.new('aes-256-gcm').decrypt
cipher.iv = iv # string 12 len
cipher.key = key # string 32 len
cipher.auth_data = # string 12 len
cipher.auth_tag = auth_tag
JSON.parse(cipher.update(cipher_text) + cipher.final)

我希望能够在go中进行加密,并尝试在ruby中进行解密。

ejk8hzay

ejk8hzay1#

您希望您的加密流如下所示:

func encrypt(in []byte, key []byte) (out []byte, err error) {

    c, err := aes.NewCipher(key)
    if err != nil {
        return
    }

    gcm, err := cipher.NewGCM(c)
    if err != nil {
        return
    }

    nonce := make([]byte, gcm.NonceSize())
    if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
        return
    }

    out = gcm.Seal(nonce, nonce, in, nil) // include the nonce in the preable of 'out'
    return
}

你的意见 key 根据aes.newcipher文档,长度应为16、24或32字节。
加密的 out 来自上述函数的字节将包括 nonce 前缀(长度为16、24或32字节)-因此可以在解密阶段轻松提取,如下所示:

// `in` here is ciphertext
nonce, ciphertext := in[:ns], in[ns:]

哪里 ns 是这样计算的:

c, err := aes.NewCipher(key)
if err != nil {
    return
}

gcm, err := cipher.NewGCM(c)
if err != nil {
    return
}

ns := gcm.NonceSize()
if len(in) < ns {
    err = fmt.Errorf("missing nonce - input shorter than %d bytes", ns)
    return
}

编辑:
如果您在计算机上加密 go 默认密码设置的一方(见上文):

gcm, err := cipher.NewGCM(c)

从源代码中,标记字节大小将为 16 .
注意:如果使用cipher.newgcmwithtagsize,则大小将明显不同(基本上介于 1216 字节)
因此,让我们假设标记大小为 16 ,掌握了这些知识,并且知道完整的有效载荷布置是:

IV/nonce + raw_ciphertext + auth_tag

这个 auth_tagRuby 用于解密的一方是有效负载的最后16个字节;原始密文是在 IV/nonce 直到 auth_tag 开始。

ipakzgxi

ipakzgxi2#

aesgcm.Seal 在密文末尾自动附加gcm标记。您可以在源代码中看到它:

var tag [gcmTagSize]byte
    g.auth(tag[:], out[:len(plaintext)], data, &tagMask)
    copy(out[len(plaintext):], tag[:])                   // <---------------- here

这样你就完成了,你不需要其他任何东西了。 gcm.Seal 已返回结尾附加了auth标记的密文。
类似地,您不需要为其提取auth标记 gcm.Open ,它也会自动执行:

tag := ciphertext[len(ciphertext)-g.tagSize:]        // <---------------- here
    ciphertext = ciphertext[:len(ciphertext)-g.tagSize]

因此,解密过程中需要做的就是提取iv(nonce)并将其余部分作为密文传递。

相关问题