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

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

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

问题

  1. 我需要在Node.js / JavaScript服务器和用Rust编写的客户端之间建立客户端-服务器连接。消息必须使用AES-GCM-256加密。在Rust(版本1.67.0),我使用[aes-gcm crate][1]
  2. 以下是Rust代码引发的错误`aead::Error`。这里的`cipher.decrypt()`有什么问题?
  3. 我确信Node.js实现是正确的。我认为Rust代码中的`key`变量应该与Node.js中的相同的密钥`12341234123412341234123412341234`)。
  4. **Node.js /发送方实现**
  5. ```javascript
  6. const crypto = require('crypto')
  7. const aes256gcm = (key) => {
  8. const encrypt = (str) => {
  9. const iv = crypto.randomBytes(12);
  10. const ivString = iv.toString("base64")
  11. const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  12. let enc1 = cipher.update(str, 'utf8');
  13. let enc2 = cipher.final();
  14. let result = Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString("base64");
  15. return { result, iv: ivString }
  16. };
  17. const decrypt = (enc) => {
  18. enc = Buffer.from(enc, "base64");
  19. const iv = enc.slice(enc.length - 28, enc.length - 16);
  20. const tag = enc.slice(enc.length - 16);
  21. enc = enc.slice(0, enc.length - 28);
  22. const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
  23. decipher.setAuthTag(tag);
  24. let str = decipher.update(enc, null, 'utf8');
  25. str += decipher.final('utf8');
  26. return str;
  27. };
  28. return {
  29. encrypt,
  30. decrypt,
  31. };
  32. };
  33. const cipher = aes256gcm("12341234123412341234123412341234"); // 仅作测试,密钥必须是32位
  34. const ct = cipher.encrypt('Hello world!!!');
  35. console.log("加密消息:", ct.result)
  36. console.log("iv / 随机数:", ct.iv)
  37. const pt = cipher.decrypt(ct.result);
  38. console.log("解密消息:", pt); // 这个工作得很好!

Node.js加密结果

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

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

  1. use aes_gcm::{
  2. aead::{Aead, KeyInit, OsRng},
  3. Aes256Gcm, Nonce,
  4. };
  5. use base64::{engine::general_purpose, Engine as _};
  6. fn main() {
  7. // 来自发送方的主密钥
  8. let master = "12341234123412341234123412341234".as_bytes();
  9. let cipher = Aes256Gcm::new_from_slice(master).unwrap();
  10. // 来自发送方的随机数 / 随机向量
  11. let nonce_str_base64 = "fvXWsQhmTtDJsGFv";
  12. let nonce_str: Vec<u8> = general_purpose::STANDARD.decode(nonce_str_base64).unwrap();
  13. let nonce = Nonce::from_slice(&nonce_str); // 96位;每条消息唯一
  14. // 来自发送方的加密文本
  15. let ciphertext_base64 = "Zf5aB0bbVGGX3k9Yt6x+9daxCGZO0MmwYW8VUsOY4j3gNYXP47hvfGgd";
  16. let ciphertext = general_purpose::STANDARD.decode(ciphertext_base64).unwrap();
  17. // 在这里得到aead::Error
  18. match cipher.decrypt(nonce, ciphertext.as_slice()) {
  19. Ok(decrypted) => {
  20. let result = String::from_utf8(decrypted).unwrap();
  21. println!("结果:{}", result);
  22. }
  23. Err(err) => print!("{}", err), // 打印错误:aead::Error
  24. };
  25. }
  1. <details>
  2. <summary>英文:</summary>
  3. 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].
  4. The Rust code below throws an error: `aead::Error`. What&#39;s wrong with the `cipher.decrypt()` here ?
  5. 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`)
  6. **Implementation in Node.js / sender side**

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

  1. const encrypt = (str) =&gt; {
  2. const iv = new crypto.randomBytes(12);
  3. const ivString = iv.toString(&quot;base64&quot;)
  4. const cipher = crypto.createCipheriv(&#39;aes-256-gcm&#39;, key, iv);
  5. let enc1 = cipher.update(str, &#39;utf8&#39;);
  6. let enc2 = cipher.final();
  7. let result = Buffer.concat([enc1, enc2, iv, cipher.getAuthTag()]).toString(&quot;base64&quot;);
  8. return { result, iv: ivString }
  9. };
  10. const decrypt = (enc) =&gt; {
  11. enc = Buffer.from(enc, &quot;base64&quot;);
  12. const iv = enc.slice(enc.length - 28, enc.length - 16);
  13. const tag = enc.slice(enc.length - 16);
  14. enc = enc.slice(0, enc.length - 28);
  15. const decipher = crypto.createDecipheriv(&#39;aes-256-gcm&#39;, key, iv);
  16. decipher.setAuthTag(tag);
  17. let str = decipher.update(enc, null, &#39;utf8&#39;);
  18. str += decipher.final(&#39;utf8&#39;);
  19. return str;
  20. };
  21. return {
  22. encrypt,
  23. decrypt,
  24. };

};

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!

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

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

fn main() {

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

}

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

改成了这个:

  1. 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:

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

to this:

  1. 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:

确定