从密码 aes 256 GCM Golang 中提取标签

itx*_*itx 4 ruby go aes-gcm

我用Ruby进行了加密和解密,并尝试用Go重写。我一步一步尝试,所以从 ruby​​ 中的加密开始,并尝试在 go 中解密,它是有效的。但是当我尝试在 Go 中编写加密并在 ruby​​ 中解密时。当尝试提取标签时我陷入困境,我解释了为什么我需要提取身份验证标签

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
Run Code Online (Sandbox Code Playgroud)

我尝试连接初始向量、密文和身份验证标签,因此在解密之前我可以提取它们,特别是身份验证标签,因为我需要在 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)))
Run Code Online (Sandbox Code Playgroud)

如何提取身份验证标签并将其与 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)
Run Code Online (Sandbox Code Playgroud)

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

col*_*tor 5

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

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
}
Run Code Online (Sandbox Code Playgroud)

根据aes.NewCipher文档,您的输入key长度应为 16、24 或 32 字节。

out上述函数中的加密字节将包含nonce前缀(长度为 16、24 或 32 字节) - 因此可以在解密阶段轻松提取它,如下所示:

// `in` here is ciphertext
nonce, ciphertext := in[:ns], in[ns:]
Run Code Online (Sandbox Code Playgroud)

其中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
}
Run Code Online (Sandbox Code Playgroud)

编辑

如果您go使用默认密码设置进行加密(见上文):

gcm, err := cipher.NewGCM(c)
Run Code Online (Sandbox Code Playgroud)

源代码来看,标签字节大小将为16

注意:如果使用cipher.NewGCMWithTagSize - 那么大小显然会有所不同(基本上是字节之间的任何12位置16

因此,我们假设标签大小为16,并了解了这些知识,并且知道完整的有效负载排列为:

IV/nonce + raw_ciphertext + auth_tag
Run Code Online (Sandbox Code Playgroud)

解密一侧是有效负载的最后 16 个字节auth_tagRubyraw_ciphertext 是从IV/nonce开始之后的所有字节auth_tag