如何在Rust中使用指定字节密钥实现AES-GCM解密?
解决Python AES-GCM解密迁移到Rust的问题
问题核心梳理
你在迁移过程中遇到的三个问题,本质都是数据处理流程不匹配和Rust API使用误区,逐个拆解解决:
1. 密钥长度问题:从267字节到32字节的正确处理
问题原因
你拿到的267字节是未处理的encrypted_key(base64解码后的数据),Python中会先去掉前5字节的DPAPI前缀,再通过CryptUnprotectData解密得到标准AES-256所需的32字节密钥。
Rust实现方案
用dpapi crate直接实现Windows DPAPI解密,替代Python的win32crypt.CryptUnprotectData:
use base64::engine::{general_purpose, Engine as _}; use dpapi::UnprotectData; use serde_json::Value; use std::fs; fn get_master_key(path: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> { // 读取并解析Local State文件 let local_state = fs::read_to_string(path)?; let json: Value = serde_json::from_str(&local_state)?; // 提取base64编码的加密密钥并解码 let encrypted_key_b64 = json["os_crypt"]["encrypted_key"].as_str().ok_or("无效的encrypted_key")?; let mut encrypted_key = general_purpose::STANDARD.decode(encrypted_key_b64)?; // 移除前5字节的DPAPI前缀 encrypted_key.drain(0..5); // 调用DPAPI解密得到32字节的AES密钥 let protected_data = encrypted_key.as_slice().unprotect_data(None, None)?; Ok(protected_data.0) }
将处理后的密钥转为Rust AES-GCM所需的格式:
use aes_gcm::{Aes256Gcm, Key}; // master_key是get_master_key返回的Vec<u8>,长度固定32 let key = Key::<Aes256Gcm>::from_slice(&master_key); let cipher = Aes256Gcm::new(key);
2. aead::Error排查:输入数据处理错误是核心
最关键错误:未做base64解码
Python代码会先对匹配到的字符串(冒号后部分)做base64解码,再提取IV和密文;但你的Rust代码直接用原始字符串的字节处理,完全不符合数据格式要求,这是解密失败的主要原因。
修正后的解密函数
use aes_gcm::{Aes256Gcm, Key, Nonce, aead::{Aead, KeyInit}}; use base64::engine::{general_purpose, Engine as _}; fn decrypt_encrypted_strings(input_str: &str, master_key: &[u8]) -> Result<String, Box<dyn std::error::Error>> { // 分割出冒号后的base64编码部分 let (_, b64_part) = input_str.split_once(":").ok_or("输入格式错误")?; // base64解码得到原始加密字节数组 let buff = general_purpose::STANDARD.decode(b64_part)?; // 提取12字节的IV(GCM标准nonce长度) let nonce = Nonce::from_slice(&buff[3..15]); // 提取密文+Tag(GCM中Tag默认16字节,附在密文后) let ciphertext_with_tag = &buff[15..]; // 创建AES-GCM实例并解密(自动验证Tag) let key = Key::<Aes256Gcm>::from_slice(master_key); let cipher = Aes256Gcm::new(key); let plaintext = cipher.decrypt(nonce, ciphertext_with_tag)?; // 将明文转为UTF-8字符串 let plaintext_str = String::from_utf8(plaintext)?; Ok(plaintext_str) }
其他可能的错误点
- 如果仍出现
aead::Error,优先检查:- Rust解码后的
buff与Python中base64.b64decode结果是否完全一致 - 处理后的master key是否和Python输出的密钥完全相同
- 输入字符串是否完整包含
dQw4w9WgXcQ:前缀和后续的base64内容
- Rust解码后的
3. 调用代码修正
确保传入正确的输入数据和处理后的密钥:
// 先获取处理好的master key let master_key = get_master_key("path/to/Local State").unwrap(); let mut decrypted_strings = Vec::new(); for line in s.lines() { if let Some(mat) = re.captures(line) { let input_str = mat.get(0).unwrap().as_str(); match decrypt_encrypted_strings(input_str, &master_key) { Ok(text) => { if !decrypted_strings.contains(&text) { decrypted_strings.push(text); } } Err(e) => eprintln!("解密失败: {}", e), } } }
Cargo.toml依赖配置
添加所需的crate:
[dependencies] aes-gcm = "0.10.1" base64 = "0.21.5" dpapi = "0.4.0" serde_json = "1.0.108" regex = "1.10.2"
内容的提问来源于stack exchange,提问作者martin
相关产品推荐
相关产品推荐

