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

huangapple go评论87阅读模式
英文:

Extract tag from cipher aes 256 GCM Golang

问题

我已经翻译了你提供的内容,请查看以下翻译结果:

我在Ruby中有加密和解密的代码,并尝试用Go重写。我逐步尝试,所以从Ruby中的加密开始,然后尝试在Go中解密,这部分是可以工作的。但是当我尝试在Go中编写加密并在Ruby中解密时遇到了困难。当我尝试提取标签时卡住了,我解释一下为什么需要提取认证标签。

Ruby中的加密代码如下:

plaintext = "Foo bar"
cipher = OpenSSL::Cipher.new('aes-256-gcm')
cipher.encrypt
cipher.iv = iv # 字符串,长度为12
cipher.key = key # 字符串,长度为32
cipher.auth_data = # 字符串,长度为12
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时自动进行验证。

下面是Go中的加密函数:

ciphertext := aesgcm.Seal(nil, []byte(iv), []byte(plaintext), []byte(authData))
src := iv + string(ciphertext) // + 在这里尝试添加认证标签
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 # 字符串,长度为12
cipher.key = key # 字符串,长度为32
cipher.auth_data = # 字符串,长度为12
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. I try step by step, so start with encryption in ruby and try to decryption in go, it's works. But when I try to write encryption in Go and decrypt in ruby. I'm stuck when try to extract the tag, I explain the reason why I need extract the auth tag

Encryption In 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

I try to concatenate an initial vector, a ciphertext and the authentication tag, so before decrypt I can extract them, especially the authentication tag, because I need set it before calling Cipher#final in Ruby

auth_tag

> The tag must be set after calling Cipher#decrypt, Cipher#key= and
> Cipher#iv=, but before calling Cipher#final. After all decryption is
> performed, the tag is verified automatically in the call to
> Cipher#final

Here is function encryption in 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)))

How can I extract the authentication tag and concatenate it with iv and ciphertext, so I can decrypt with decryption function in 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)

I want to be able doing encryption in Go, and try to decryption in Ruby.

答案1

得分: 4

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

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) // 将nonce包含在out的前导中
    return
}

根据aes.NewCipher文档,你的输入key应该是16、24或32字节长。

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

// 这里的`in`是密文
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

Ruby端进行解密时,auth_tag是有效负载的最后16个字节;而raw_ciphertext是从IV/nonce之后的所有字节,直到auth_tag开始的位置。

英文:

You want your encryption flow to be something like this:

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 &#39;out&#39;
	return
}

Your input key should be of 16, 24 or 32 bytes in length as per aes.NewCipher docs.

The encrypted out bytes from the above function will include the nonce prefix (16, 24 or 32 bytes in length) - so it can be easily extracted during the decryption stage like so:

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

where ns is computed like so:

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

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

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

EDIT:

If you are encrypting on the go side with defaults cipher settings (see above):

gcm, err := cipher.NewGCM(c)

from the source the tag byte-size will be 16.

Note: if cipher.NewGCMWithTagSize is used - then the size will obviously be different (basically anywhere between 12 to 16 bytes)

So lets assume the tag size is 16, armed with this knowledge, and knowing the full payload arrangement is:

IV/nonce + raw_ciphertext + auth_tag

the auth_tag on the Ruby side for decryption, is the final 16 bytes of the payload; and the raw_ciphertext is all bytes after the IV/nonce up until where the auth_tag begins.

答案2

得分: 3

aesgcm.Seal函数会自动在密文的末尾添加GCM标签。你可以在源代码中看到这一点:

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

所以,你已经完成了,不需要其他操作。gcm.Seal函数已经返回了在密文末尾添加了认证标签的密文。

同样,对于gcm.Open函数,你也不需要提取认证标签,它会自动处理,参考链接

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

因此,在解密过程中,你只需要提取IV(nonce),然后将剩余部分作为密文传入即可。

英文:

aesgcm.Seal automatically appends the GCM tag at the end of the ciphertext. You can see it in the source:

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

So you're done, you don't need anything else. gcm.Seal already returns the ciphertext with the auth tag appended at the end.

Similarly you don't need to extract the auth tag for gcm.Open, it does it automatically, too:

    tag := ciphertext[len(ciphertext)-g.tagSize:]        // &lt;---------------- here
    ciphertext = ciphertext[:len(ciphertext)-g.tagSize]

So all you have to do during decryption is to extract the IV (nonce) and pass the rest in as ciphertext.

huangapple
  • 本文由 发表于 2021年7月12日 23:48:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/68350301.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定