Rust使用Kyber1024时如何传入自定义字符串生成对应密文
核心原因说明
Kyber1024属于密钥封装机制(KEM),并非通用公钥加密算法,原生不支持直接传入自定义字符串输出对应密文。它的设计目标是在通信双方之间协商出一份一致、第三方无法窃取的固定长度对称密钥,要实现自定义字符串加密,需要搭配对称认证加密(AEAD)算法做混合加密,这也是后量子加密场景的标准使用模式。
实现流程
- 接收方生成Kyber1024公私钥对,公钥公开分发,私钥本地安全存储
- 发送方获取接收方公钥后,调用
encapsulate接口得到Kyber层密文和32字节长度的共享密钥 - 发送方以该共享密钥作为AES-256-GCM的加密密钥,对自定义字符串做对称加密,同时生成12字节随机nonce
- 发送方将「Kyber层密文 + nonce + 对称加密后的字符串密文」打包为总密文,发送给接收方
- 接收方收到总密文后,先用本地私钥调用
decapsulate接口解封装Kyber层密文,得到和发送方一致的32字节共享密钥 - 接收方用该共享密钥解密对称密文,得到原始自定义字符串
具体代码实现
依赖配置
在Cargo.toml中添加以下依赖:
[dependencies] pqcrypto-kyber = "0.7.6" aes-gcm = "0.10.3" rand_core = { version = "0.6.4", features = ["std"] }
核心逻辑代码
use pqcrypto_kyber::kyber1024::{ self, keypair, encapsulate, decapsulate, PublicKey, SecretKey, Ciphertext as KyberCiphertext }; use aes_gcm::{ aead::{Aead, KeyInit}, Aes256Gcm, Nonce }; use rand_core::{OsRng, RngCore}; use std::error::Error; // 传输用组合密文结构 pub struct CombinedCiphertext { kyber_ct: KyberCiphertext, aes_nonce: [u8; 12], // AES-GCM标准nonce长度为12字节,无需保密 encrypted_payload: Vec<u8>, // 自定义字符串加密后的密文 } /// 传入接收方公钥和待加密字符串,返回可直接传输的组合密文 pub fn encrypt_string(recipient_pk: &PublicKey, plaintext: &str) -> Result<CombinedCiphertext, Box<dyn Error>> { // Kyber层封装得到共享密钥和对应密文 let (shared_key, kyber_ct) = encapsulate(recipient_pk); // 初始化AES-256-GCM加密器,Kyber1024输出的共享密钥刚好32字节,匹配AES256密钥长度要求 let cipher = Aes256Gcm::new_from_slice(shared_key.as_bytes())?; // 生成随机nonce let mut aes_nonce = [0u8; 12]; OsRng.fill_bytes(&mut aes_nonce); let nonce = Nonce::from_slice(&aes_nonce); // 加密自定义字符串 let encrypted_payload = cipher.encrypt(nonce, plaintext.as_bytes())?; Ok(CombinedCiphertext { kyber_ct, aes_nonce, encrypted_payload, }) } /// 传入本地私钥和收到的组合密文,返回解密后的原始字符串 pub fn decrypt_string(recipient_sk: &SecretKey, combined_ct: &CombinedCiphertext) -> Result<String, Box<dyn Error>> { // Kyber层解封装得到相同的共享密钥 let shared_key = decapsulate(&combined_ct.kyber_ct, recipient_sk); // 初始化解密器 let cipher = Aes256Gcm::new_from_slice(shared_key.as_bytes())?; let nonce = Nonce::from_slice(&combined_ct.aes_nonce); // 解密并转换为字符串 let plaintext_bytes = cipher.decrypt(nonce, combined_ct.encrypted_payload.as_ref())?; let plaintext = String::from_utf8(plaintext_bytes)?; Ok(plaintext) }
使用示例
fn main() -> Result<(), Box<dyn Error>> { // 接收方本地生成公私钥对,公钥公开给发送方,私钥不得泄露 let (pk, sk) = keypair(); // 发送方逻辑:加密自定义字符串 let secret_msg = "测试自定义加密字符串,包含中文、数字123、符号!@#"; let combined_ct = encrypt_string(&pk, secret_msg)?; // 此处可将combined_ct序列化为字节流通过网络发送,序列化可选用bincode/postcard等库 // 接收方逻辑:解密收到的密文 let decrypted_msg = decrypt_string(&sk, &combined_ct)?; assert_eq!(secret_msg, decrypted_msg); println!("解密成功,原始内容:{}", decrypted_msg); Ok(()) }
注意事项
- 禁止修改Kyber算法内部逻辑强行传入自定义字符串,会直接破坏算法的后量子安全属性
- 对称加密必须选择带身份认证的AEAD算法,禁止使用无认证的CBC/ECB等加密模式,避免密文被篡改、伪造
- Kyber1024输出的共享密钥固定为32字节,无需额外做密钥派生即可直接用于AES-256加密
- 组合密文传输前需要做序列化处理,Kyber1024密文固定长度为1568字节,加12字节nonce,固定加密开销很小
内容的提问来源于stack exchange,提问作者user19316228
相关产品推荐
相关产品推荐

