如何解密使用AES-GCM-256加密的消息?

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

How to decrypt message that's encrypted in AES-GCM-256?

问题

我需要在Node.js / JavaScript服务器和用Rust编写的客户端之间建立客户端-服务器连接。消息必须使用AES-GCM-256加密。在Rust中(版本1.67.0),我使用[aes-gcm crate][1]

以下是Rust代码引发的错误:`aead::Error`。这里的`cipher.decrypt()`有什么问题?

我确信Node.js实现是正确的。我认为Rust代码中的`key`变量应该与Node.js中的相同的密钥(`12341234123412341234123412341234`)。

**Node.js /发送方实现**

```javascript
const crypto = require('crypto')
const aes256gcm = (key) => {

    const encrypt = (str) => {
        const iv = crypto.randomBytes(12);
        const ivString = iv.toString("base64")
        const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
        let enc1 = cipher.update(str, 'utf8');
        let enc2 = cipher.final();
        let result = Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString("base64");
        return { result, iv: ivString }
    };

    const decrypt = (enc) => {
        enc = Buffer.from(enc, "base64");
        const iv = enc.slice(enc.length - 28, enc.length - 16);
        const tag = enc.slice(enc.length - 16);
        enc = enc.slice(0, enc.length - 28);
        const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
        decipher.setAuthTag(tag);
        let str = decipher.update(enc, null, 'utf8');
        str += decipher.final('utf8');
        return str;
    };

    return {
        encrypt,
        decrypt,
    };
};

const cipher = aes256gcm("12341234123412341234123412341234"); // 仅作测试,密钥必须是32位
const ct = cipher.encrypt('Hello world!!!');
console.log("加密消息:", ct.result)
console.log("iv / 随机数:", ct.iv)

const pt = cipher.decrypt(ct.result);
console.log("解密消息:", pt); // 这个工作得很好!

Node.js加密结果

加密消息:  Zf5aB0bbVGGX3k9Yt6x+9daxCGZO0MmwYW8VUsOY4j3gNYXP47hvfGgd
iv / 随机数:  fvXWsQhmTtDJsGFv
解密消息:  Hello world!!!

解密部分(Rust)/接收方实现

use aes_gcm::{
    aead::{Aead, KeyInit, OsRng},
    Aes256Gcm, Nonce,
};
use base64::{engine::general_purpose, Engine as _};

fn main() {

    // 来自发送方的主密钥  
    let master = "12341234123412341234123412341234".as_bytes();
    let cipher = Aes256Gcm::new_from_slice(master).unwrap();

    // 来自发送方的随机数 / 随机向量
    let nonce_str_base64 = "fvXWsQhmTtDJsGFv";
    let nonce_str: Vec<u8> = general_purpose::STANDARD.decode(nonce_str_base64).unwrap();
    let nonce = Nonce::from_slice(&nonce_str); // 96位;每条消息唯一

    // 来自发送方的加密文本
    let ciphertext_base64 = "Zf5aB0bbVGGX3k9Yt6x+9daxCGZO0MmwYW8VUsOY4j3gNYXP47hvfGgd";
    let ciphertext = general_purpose::STANDARD.decode(ciphertext_base64).unwrap();

    // 在这里得到aead::Error
    match cipher.decrypt(nonce, ciphertext.as_slice()) {
        Ok(decrypted) => {
            let result = String::from_utf8(decrypted).unwrap();
            println!("结果:{}", result);
        }
        Err(err) => print!("{}", err), // 打印错误:aead::Error
    };
}

<details>
<summary>英文:</summary>
I need to make a client-server connection from node.js/javascript server to a client written in Rust. The message had to be encrypted with AES-GCM-256. While in Rust (version 1.67.0) I use [aes-gcm crate][1].
The Rust code below throws an error: `aead::Error`. What&#39;s wrong with the `cipher.decrypt()` here ?
I&#39;m sure that the node.js implementation is correct. I think that the `key` variable in Rust code should be the same key from the node.js (`12341234123412341234123412341234`) 
**Implementation in Node.js / sender side**

const crypto = require('crypto')
const aes256gcm = (key) => {

const encrypt = (str) =&gt; {
const iv = new crypto.randomBytes(12);
const ivString = iv.toString(&quot;base64&quot;)
const cipher = crypto.createCipheriv(&#39;aes-256-gcm&#39;, key, iv);
let enc1 = cipher.update(str, &#39;utf8&#39;);
let enc2 = cipher.final();
let result = Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString(&quot;base64&quot;);
return { result, iv: ivString }
};
const decrypt = (enc) =&gt; {
enc = Buffer.from(enc, &quot;base64&quot;);
const iv = enc.slice(enc.length - 28, enc.length - 16);
const tag = enc.slice(enc.length - 16);
enc = enc.slice(0, enc.length - 28);
const decipher = crypto.createDecipheriv(&#39;aes-256-gcm&#39;, key, iv);
decipher.setAuthTag(tag);
let str = decipher.update(enc, null, &#39;utf8&#39;);
str += decipher.final(&#39;utf8&#39;);
return str;
};
return {
encrypt,
decrypt,
};

};

const cipher = aes256gcm("12341234123412341234123412341234"); // just a test key must be 32
const ct = cipher.encrypt('Hello world!!!');
console.log("encrypted message: ", ct.result)
console.log("iv / nonce : ", ct.iv)

const pt = cipher.decrypt(ct.result);
console.log("decrypted message: ", pt); // this works flawlessly!


**Encryption Result by Node.js**
encrypted message:  Zf5aB0bbVGGX3k9Yt6x+9daxCGZO0MmwYW8VUsOY4j3gNYXP47hvfGgd
iv / nonce       :  fvXWsQhmTtDJsGFv
decrypted message:  Hello world!!!
**The decryption part (Rust) / Receiver Side**

use aes_gcm::{
aead::{Aead, KeyInit, OsRng},
Aes256Gcm, Nonce,
};
use base64::{engine::general_purpose, Engine as _};

fn main() {

// master key from sender  
let master = &quot;12341234123412341234123412341234&quot;.as_bytes();
let cipher = Aes256Gcm::new_from_slice(master).unwrap();
// nonce / iv from sender
let nonce_str_base64 = &quot;fvXWsQhmTtDJsGFv&quot;;
let nonce_str: Vec&lt;u8&gt; = general_purpose::STANDARD.decode(nonce_str_base64).unwrap();
let nonce = Nonce::from_slice(&amp;nonce_str); // 96-bits; unique per message
// encrypted text from sender
let ciphertext_base64 = &quot;Zf5aB0bbVGGX3k9Yt6x+9daxCGZO0MmwYW8VUsOY4j3gNYXP47hvfGgd&quot;;
let ciphertext = general_purpose::STANDARD.decode(ciphertext_base64).unwrap();
// gets aead::Error here
match cipher.decrypt(nonce, ciphertext.as_slice()) {
Ok(decrypted) =&gt; {
let result = String::from_utf8(decrypted).unwrap();
println!(&quot;result: {}&quot;, result);
}
Err(err) =&gt; print!(&quot;{}&quot;, err), &lt;--- prints error: aead::Error
};

}


[1]: https://docs.rs/aes-gcm/latest/aes_gcm/index.html
</details>
# 答案1
**得分**: 0
AES-256-GCM由三个部分组成:
1. `payload` 或加密文本,
2. `iv` 或 nonce,一个仅生成一次的唯一随机数,
3. 以及 `tag`,它是认证的一部分,用于确保加密消息未被更改。
[aes_gcm][1] crate 使用 `payload` + `tag` 进行解密。因此,解决方案是从结果消息中移除 `iv` 部分。我只是在发送方(Node.js)上进行了更改:
从这个:
```javascript
let result = Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString("base64");

改成了这个:

let result = Buffer.concat([enc1, enc2, cipher.getAuthTag()]).toString("base64");

nonce/iv 部分通过函数参数提供给 cipher.decrypt

英文:

AES-256-GCM consists of three parts:

  1. payload or ciphered text,
  2. iv or nonce, a unique random number that generated once
  3. and the tag that is part of the authentication that ensures the encrypted message has not been altered

The aes_gcm crate uses payload + tag for decryption. So the solution is to remove the iv part from the result message. I just change the sender side (Node.js) from this:

let result = Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString(&quot;base64&quot;);

to this:

let result = Buffer.concat([enc1, enc2, cipher.getAuthTag()]).toString(&quot;base64&quot;);

the nonce/iv part is supplied to cipher.decrypt through function parameter

huangapple
  • 本文由 发表于 2023年2月6日 11:29:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75357087.html
匿名

发表评论

匿名网友

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

确定