【问题标题】:Extract tag from cipher aes 256 GCM Golang从密码 aes 256 GCM Golang 中提取标签
【发布时间】:2021-07-12 15:48:49
【问题描述】:

我在 Ruby 中进行了加密和解密,并尝试用 Go 重写。我一步一步尝试,所以从 ruby​​ 中的加密开始,然后尝试在 go 中解密,它是有效的。但是当我尝试在 Go 中编写 encryption 并在 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 之前设置它

auth_tag

必须在调用 Cipher#decrypt、Cipher#key= 和 Cipher#iv=,但在调用 Cipher#final 之前。毕竟解密是 执行,标签在调用中自动验证 密码#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 中解密。

【问题讨论】:

  • 你能阐明你的最终目标吗?一开始你说I have encryption and decryption in Ruby and try to rewrite with Go. - 但最后你想decrypt with decryption function in ruby。您是否在 go 端加密 - 然后想在 ruby 端解密?
  • @colm.anseo 是的..如果你看到我一步一步地讲述,我的意思是我不能用大爆炸过程,所以我已经成功地使用 ruby​​ 进行加密并使用 go for解密,我想尝试与之相反,使用 go 进行加密并使用 ruby​​ 在准确的时间进行解密

标签: ruby go aes-gcm


【解决方案1】:

aesgcm.Seal 自动在密文末尾附加 GCM 标签。你可以在source看到它:

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

所以你已经完成了,你不需要任何其他东西。 gcm.Seal 已经返回密文,并在末尾附加了 auth 标签。

同样你不需要为gcm.Open提取身份验证标签,它会自动提取too

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

因此,在解密过程中您所要做的就是提取 IV(随机数)并将其余部分作为密文传递。

【讨论】:

  • 感谢您的详细解释,当然我知道如果我使用 Go 进行解密,我不需要精确的 auth 标签,但我的情况是我使用 Go 进行加密并使用 Ruby解密,在 Ruby 中我需要在 cipher final 之前设置 auth 标签,所以我需要在 Go 中精确 auth 标签并将它与 cipher 连接起来
  • 你不需要在 Go 中提取 auth 标签。您需要在 Ruby 中提取 auth 标签。而您的 Ruby 代码已经这样做了:auth_tag = raw_data.slice(raw_data.length - 16, 16)。标签会在那里,Go 会自动把它放在那里。
【解决方案2】:

您希望您的加密流程是这样的:

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
}

根据 aes.NewCipher 文档,您的输入 key 的长度应为 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)

the source标签字节大小将是16

注意:如果使用cipher.NewGCMWithTagSize - 那么大小显然会不同(基本上在1216 字节之间的任何地方)

因此,假设标签大小为16,掌握了这些知识,并且知道完整的有效负载安排是:

IV/nonce + raw_ciphertext + auth_tag

用于解密的Ruby侧的auth_tag,是payload的最后16个字节; raw_ciphertext 是IV/nonce 之后直到auth_tag 开始的所有字节。

【讨论】:

  • 谢谢,但是如何提取标签?似乎预先添加了向量,如果您在 ruby​​ 中看到解密,我需要在调用 cipher final 之前验证标签,我在 golang 中找不到任何引用来提取标签,我认为标签会自动附加到密文在 golang 中
  • 身份验证标签在加密过程中隐式添加并在解密过程中进行验证。调用者不需要这样做,cipher 包会为您完成所有繁重的工作。
  • 绝对我在 Golang 中看到我们不需要那个,但是当我在 ruby​​ 中解密时,你可以看到验证需要那个
  • 你的意思是我只是用大小为 16 字节的切片 cipher_text 提取
  • cipher_text 是可变长度的 - 但 nonce/IVauth_tag 是固定的(或在加密类型中达成一致)。由于它们将cipher_text 夹在中间,因此在查看整个有效载荷长度时很容易计算。我已经更新了答案以反映这一点。
猜你喜欢
  • 2021-09-03
  • 2021-09-23
  • 1970-01-01
  • 2021-06-04
  • 1970-01-01
  • 2017-10-06
  • 1970-01-01
  • 2017-05-10
  • 2021-06-07
相关资源
最近更新 更多