- 发布于
Rust-Wasm加解密安全防护方案:构建高安全性的Web加密系统
- 作者

- 姓名
- 全能波
- GitHub
- @weicracker
Rust-Wasm加解密安全防护方案:构建高安全性的Web加密系统
在Web应用中,数据安全和隐私保护至关重要。Rust与WebAssembly的结合为构建高性能、高安全性的加密系统提供了理想的平台。本文将详细介绍完整的加解密安全防护方案。
项目配置和依赖
核心依赖配置
# Cargo.toml - 加密安全项目配置
[package]
name = "rust-wasm-crypto"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
js-sys = "0.3"
web-sys = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
getrandom = { version = "0.2", features = ["js"] }
console_error_panic_hook = "0.1"
wee_alloc = "0.4"
# 加密相关依赖
ring = "0.16"
aes-gcm = "0.10"
chacha20poly1305 = "0.10"
rsa = "0.9"
ed25519-dalek = "1.0"
sha2 = "0.10"
sha3 = "0.10"
blake3 = "1.0"
argon2 = "0.5"
rand = "0.8"
rand_chacha = "0.3"
zeroize = { version = "1.0", features = ["zeroize_derive"] }
subtle = "2.4"
[dependencies.web-sys]
version = "0.3"
features = [
"console",
"Crypto",
"SubtleCrypto",
"CryptoKey",
"Window",
"Performance",
]
# 优化配置
[profile.release]
opt-level = "s"
lto = true
codegen-units = 1
panic = "abort"
基础安全模块
// src/lib.rs - 核心安全模块
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
// 全局设置
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen(start)]
pub fn main() {
console_error_panic_hook::set_once();
}
// 安全随机数生成器
#[wasm_bindgen]
pub struct SecureRandom;
#[wasm_bindgen]
impl SecureRandom {
#[wasm_bindgen(constructor)]
pub fn new() -> SecureRandom {
SecureRandom
}
#[wasm_bindgen]
pub fn generate_bytes(&self, length: usize) -> Vec<u8> {
use rand::RngCore;
let mut rng = rand::thread_rng();
let mut bytes = vec![0u8; length];
rng.fill_bytes(&mut bytes);
bytes
}
#[wasm_bindgen]
pub fn generate_u32(&self) -> u32 {
use rand::RngCore;
rand::thread_rng().next_u32()
}
#[wasm_bindgen]
pub fn generate_u64(&self) -> u64 {
use rand::RngCore;
rand::thread_rng().next_u64()
}
}
// 安全内存管理
#[derive(Zeroize, ZeroizeOnDrop)]
#[wasm_bindgen]
pub struct SecureBuffer {
#[zeroize(skip)]
data: Vec<u8>,
}
#[wasm_bindgen]
impl SecureBuffer {
#[wasm_bindgen(constructor)]
pub fn new(size: usize) -> SecureBuffer {
SecureBuffer {
data: vec![0u8; size],
}
}
#[wasm_bindgen]
pub fn from_bytes(bytes: &[u8]) -> SecureBuffer {
SecureBuffer {
data: bytes.to_vec(),
}
}
#[wasm_bindgen]
pub fn len(&self) -> usize {
self.data.len()
}
#[wasm_bindgen]
pub fn as_bytes(&self) -> Vec<u8> {
self.data.clone()
}
#[wasm_bindgen]
pub fn copy_from(&mut self, source: &[u8]) {
if source.len() <= self.data.len() {
self.data[..source.len()].copy_from_slice(source);
}
}
#[wasm_bindgen]
pub fn clear(&mut self) {
self.data.zeroize();
}
}
// 哈希函数集合
#[wasm_bindgen]
pub struct HashFunctions;
#[wasm_bindgen]
impl HashFunctions {
#[wasm_bindgen(constructor)]
pub fn new() -> HashFunctions {
HashFunctions
}
#[wasm_bindgen]
pub fn sha256(&self, data: &[u8]) -> Vec<u8> {
use sha2::{Sha256, Digest};
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
#[wasm_bindgen]
pub fn sha512(&self, data: &[u8]) -> Vec<u8> {
use sha2::{Sha512, Digest};
let mut hasher = Sha512::new();
hasher.update(data);
hasher.finalize().to_vec()
}
#[wasm_bindgen]
pub fn sha3_256(&self, data: &[u8]) -> Vec<u8> {
use sha3::{Sha3_256, Digest};
let mut hasher = Sha3_256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
#[wasm_bindgen]
pub fn blake3(&self, data: &[u8]) -> Vec<u8> {
blake3::hash(data).as_bytes().to_vec()
}
#[wasm_bindgen]
pub fn hmac_sha256(&self, key: &[u8], data: &[u8]) -> Vec<u8> {
use ring::hmac;
let key = hmac::Key::new(hmac::HMAC_SHA256, key);
hmac::sign(&key, data).as_ref().to_vec()
}
}
// 密钥派生函数
#[wasm_bindgen]
pub struct KeyDerivation;
#[wasm_bindgen]
impl KeyDerivation {
#[wasm_bindgen(constructor)]
pub fn new() -> KeyDerivation {
KeyDerivation
}
#[wasm_bindgen]
pub fn pbkdf2_sha256(&self, password: &[u8], salt: &[u8], iterations: u32, output_len: usize) -> Vec<u8> {
use ring::pbkdf2;
let mut output = vec![0u8; output_len];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA256,
std::num::NonZeroU32::new(iterations).unwrap(),
salt,
password,
&mut output,
);
output
}
#[wasm_bindgen]
pub fn argon2id(&self, password: &[u8], salt: &[u8], memory_cost: u32, time_cost: u32, parallelism: u32, output_len: usize) -> Result<Vec<u8>, JsValue> {
use argon2::{Argon2, Algorithm, Version, Params};
let params = Params::new(memory_cost, time_cost, parallelism, Some(output_len))
.map_err(|e| JsValue::from_str(&e.to_string()))?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut output = vec![0u8; output_len];
argon2.hash_password_into(password, salt, &mut output)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(output)
}
#[wasm_bindgen]
pub fn hkdf_sha256(&self, ikm: &[u8], salt: &[u8], info: &[u8], output_len: usize) -> Vec<u8> {
use ring::hkdf;
let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt);
let prk = salt.extract(ikm);
let okm = prk.expand(&[info], hkdf::HKDF_SHA256).unwrap();
let mut output = vec![0u8; output_len];
okm.fill(&mut output).unwrap();
output
}
}
对称加密实现
// src/symmetric.rs - 对称加密模块
use wasm_bindgen::prelude::*;
use aes_gcm::{Aes256Gcm, Key, Nonce, aead::{Aead, NewAead}};
use chacha20poly1305::{ChaCha20Poly1305, Key as ChaChaKey, Nonce as ChaChaNonce};
use zeroize::{Zeroize, ZeroizeOnDrop};
// AES-GCM 加密器
#[derive(ZeroizeOnDrop)]
#[wasm_bindgen]
pub struct AesGcmCipher {
#[zeroize(skip)]
cipher: Aes256Gcm,
}
#[wasm_bindgen]
impl AesGcmCipher {
#[wasm_bindgen(constructor)]
pub fn new(key: &[u8]) -> Result<AesGcmCipher, JsValue> {
if key.len() != 32 {
return Err(JsValue::from_str("Key must be 32 bytes"));
}
let key = Key::from_slice(key);
let cipher = Aes256Gcm::new(key);
Ok(AesGcmCipher { cipher })
}
#[wasm_bindgen]
pub fn encrypt(&self, plaintext: &[u8], nonce: &[u8], aad: &[u8]) -> Result<Vec<u8>, JsValue> {
if nonce.len() != 12 {
return Err(JsValue::from_str("Nonce must be 12 bytes"));
}
let nonce = Nonce::from_slice(nonce);
let ciphertext = if aad.is_empty() {
self.cipher.encrypt(nonce, plaintext)
} else {
self.cipher.encrypt(nonce, aes_gcm::aead::Payload {
msg: plaintext,
aad,
})
};
ciphertext.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn decrypt(&self, ciphertext: &[u8], nonce: &[u8], aad: &[u8]) -> Result<Vec<u8>, JsValue> {
if nonce.len() != 12 {
return Err(JsValue::from_str("Nonce must be 12 bytes"));
}
let nonce = Nonce::from_slice(nonce);
let plaintext = if aad.is_empty() {
self.cipher.decrypt(nonce, ciphertext)
} else {
self.cipher.decrypt(nonce, aes_gcm::aead::Payload {
msg: ciphertext,
aad,
})
};
plaintext.map_err(|e| JsValue::from_str(&e.to_string()))
}
}
// ChaCha20-Poly1305 加密器
#[derive(ZeroizeOnDrop)]
#[wasm_bindgen]
pub struct ChaCha20Poly1305Cipher {
#[zeroize(skip)]
cipher: ChaCha20Poly1305,
}
#[wasm_bindgen]
impl ChaCha20Poly1305Cipher {
#[wasm_bindgen(constructor)]
pub fn new(key: &[u8]) -> Result<ChaCha20Poly1305Cipher, JsValue> {
if key.len() != 32 {
return Err(JsValue::from_str("Key must be 32 bytes"));
}
let key = ChaChaKey::from_slice(key);
let cipher = ChaCha20Poly1305::new(key);
Ok(ChaCha20Poly1305Cipher { cipher })
}
#[wasm_bindgen]
pub fn encrypt(&self, plaintext: &[u8], nonce: &[u8], aad: &[u8]) -> Result<Vec<u8>, JsValue> {
if nonce.len() != 12 {
return Err(JsValue::from_str("Nonce must be 12 bytes"));
}
let nonce = ChaChaNonce::from_slice(nonce);
let ciphertext = if aad.is_empty() {
self.cipher.encrypt(nonce, plaintext)
} else {
self.cipher.encrypt(nonce, chacha20poly1305::aead::Payload {
msg: plaintext,
aad,
})
};
ciphertext.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn decrypt(&self, ciphertext: &[u8], nonce: &[u8], aad: &[u8]) -> Result<Vec<u8>, JsValue> {
if nonce.len() != 12 {
return Err(JsValue::from_str("Nonce must be 12 bytes"));
}
let nonce = ChaChaNonce::from_slice(nonce);
let plaintext = if aad.is_empty() {
self.cipher.decrypt(nonce, ciphertext)
} else {
self.cipher.decrypt(nonce, chacha20poly1305::aead::Payload {
msg: ciphertext,
aad,
})
};
plaintext.map_err(|e| JsValue::from_str(&e.to_string()))
}
}
// 流加密器 (用于大文件)
#[wasm_bindgen]
pub struct StreamCipher {
cipher: AesGcmCipher,
chunk_size: usize,
}
#[wasm_bindgen]
impl StreamCipher {
#[wasm_bindgen(constructor)]
pub fn new(key: &[u8], chunk_size: usize) -> Result<StreamCipher, JsValue> {
let cipher = AesGcmCipher::new(key)?;
Ok(StreamCipher { cipher, chunk_size })
}
#[wasm_bindgen]
pub fn encrypt_stream(&self, data: &[u8], base_nonce: &[u8]) -> Result<Vec<u8>, JsValue> {
if base_nonce.len() != 8 {
return Err(JsValue::from_str("Base nonce must be 8 bytes"));
}
let mut result = Vec::new();
let mut counter = 0u32;
for chunk in data.chunks(self.chunk_size) {
// 构造唯一的nonce
let mut nonce = [0u8; 12];
nonce[..8].copy_from_slice(base_nonce);
nonce[8..].copy_from_slice(&counter.to_be_bytes());
let encrypted_chunk = self.cipher.encrypt(chunk, &nonce, &[])?;
result.extend_from_slice(&encrypted_chunk);
counter += 1;
}
Ok(result)
}
#[wasm_bindgen]
pub fn decrypt_stream(&self, data: &[u8], base_nonce: &[u8], original_chunk_size: usize) -> Result<Vec<u8>, JsValue> {
if base_nonce.len() != 8 {
return Err(JsValue::from_str("Base nonce must be 8 bytes"));
}
let mut result = Vec::new();
let mut counter = 0u32;
// 计算加密后的块大小 (原始大小 + 16字节认证标签)
let encrypted_chunk_size = original_chunk_size + 16;
for chunk in data.chunks(encrypted_chunk_size) {
// 构造对应的nonce
let mut nonce = [0u8; 12];
nonce[..8].copy_from_slice(base_nonce);
nonce[8..].copy_from_slice(&counter.to_be_bytes());
let decrypted_chunk = self.cipher.decrypt(chunk, &nonce, &[])?;
result.extend_from_slice(&decrypted_chunk);
counter += 1;
}
Ok(result)
}
}
非对称加密和数字签名
// src/asymmetric.rs - 非对称加密和数字签名
use wasm_bindgen::prelude::*;
use rsa::{RsaPrivateKey, RsaPublicKey, PaddingScheme, PublicKey, Hash};
use ed25519_dalek::{Keypair, PublicKey as Ed25519PublicKey, SecretKey, Signature, Signer, Verifier};
use rand::rngs::OsRng;
use zeroize::{Zeroize, ZeroizeOnDrop};
// RSA 密钥对
#[derive(ZeroizeOnDrop)]
#[wasm_bindgen]
pub struct RsaKeyPair {
#[zeroize(skip)]
private_key: RsaPrivateKey,
#[zeroize(skip)]
public_key: RsaPublicKey,
}
#[wasm_bindgen]
impl RsaKeyPair {
#[wasm_bindgen(constructor)]
pub fn new(bits: usize) -> Result<RsaKeyPair, JsValue> {
let mut rng = OsRng;
let private_key = RsaPrivateKey::new(&mut rng, bits)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
let public_key = RsaPublicKey::from(&private_key);
Ok(RsaKeyPair { private_key, public_key })
}
#[wasm_bindgen]
pub fn from_private_key_der(der: &[u8]) -> Result<RsaKeyPair, JsValue> {
let private_key = RsaPrivateKey::from_pkcs8_der(der)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
let public_key = RsaPublicKey::from(&private_key);
Ok(RsaKeyPair { private_key, public_key })
}
#[wasm_bindgen]
pub fn get_public_key_der(&self) -> Result<Vec<u8>, JsValue> {
use rsa::pkcs8::EncodePublicKey;
self.public_key.to_public_key_der()
.map(|der| der.as_bytes().to_vec())
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn get_private_key_der(&self) -> Result<Vec<u8>, JsValue> {
use rsa::pkcs8::EncodePrivateKey;
self.private_key.to_pkcs8_der()
.map(|der| der.as_bytes().to_vec())
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, JsValue> {
let mut rng = OsRng;
let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
self.public_key.encrypt(&mut rng, padding, plaintext)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, JsValue> {
let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
self.private_key.decrypt(padding, ciphertext)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, JsValue> {
let mut rng = OsRng;
let padding = PaddingScheme::new_pss::<sha2::Sha256, _>(&mut rng);
self.private_key.sign(padding, message)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool, JsValue> {
let mut rng = OsRng;
let padding = PaddingScheme::new_pss::<sha2::Sha256, _>(&mut rng);
match self.public_key.verify(padding, message, signature) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
}
// Ed25519 密钥对
#[derive(ZeroizeOnDrop)]
#[wasm_bindgen]
pub struct Ed25519KeyPair {
#[zeroize(skip)]
keypair: Keypair,
}
#[wasm_bindgen]
impl Ed25519KeyPair {
#[wasm_bindgen(constructor)]
pub fn new() -> Ed25519KeyPair {
let mut csprng = OsRng{};
let keypair = Keypair::generate(&mut csprng);
Ed25519KeyPair { keypair }
}
#[wasm_bindgen]
pub fn from_secret_key(secret_key: &[u8]) -> Result<Ed25519KeyPair, JsValue> {
if secret_key.len() != 32 {
return Err(JsValue::from_str("Secret key must be 32 bytes"));
}
let secret = SecretKey::from_bytes(secret_key)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
let public = Ed25519PublicKey::from(&secret);
let keypair = Keypair { secret, public };
Ok(Ed25519KeyPair { keypair })
}
#[wasm_bindgen]
pub fn get_public_key(&self) -> Vec<u8> {
self.keypair.public.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn get_secret_key(&self) -> Vec<u8> {
self.keypair.secret.to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn sign(&self, message: &[u8]) -> Vec<u8> {
self.keypair.sign(message).to_bytes().to_vec()
}
#[wasm_bindgen]
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool, JsValue> {
if signature.len() != 64 {
return Err(JsValue::from_str("Signature must be 64 bytes"));
}
let sig = Signature::from_bytes(signature)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
match self.keypair.public.verify(message, &sig) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
}
// 公钥验证器 (只用于验证签名)
#[wasm_bindgen]
pub struct Ed25519Verifier {
public_key: Ed25519PublicKey,
}
#[wasm_bindgen]
impl Ed25519Verifier {
#[wasm_bindgen(constructor)]
pub fn new(public_key: &[u8]) -> Result<Ed25519Verifier, JsValue> {
if public_key.len() != 32 {
return Err(JsValue::from_str("Public key must be 32 bytes"));
}
let public_key = Ed25519PublicKey::from_bytes(public_key)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
Ok(Ed25519Verifier { public_key })
}
#[wasm_bindgen]
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool, JsValue> {
if signature.len() != 64 {
return Err(JsValue::from_str("Signature must be 64 bytes"));
}
let sig = Signature::from_bytes(signature)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
match self.public_key.verify(message, &sig) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
}
安全协议和密钥交换
// src/protocols.rs - 安全协议实现
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
use zeroize::{Zeroize, ZeroizeOnDrop};
use ring::agreement::{EphemeralPrivateKey, PublicKey, agree_ephemeral, X25519};
use ring::rand::SystemRandom;
// ECDH 密钥交换
#[derive(ZeroizeOnDrop)]
#[wasm_bindgen]
pub struct EcdhKeyExchange {
#[zeroize(skip)]
private_key: Option<EphemeralPrivateKey>,
public_key: Vec<u8>,
}
#[wasm_bindgen]
impl EcdhKeyExchange {
#[wasm_bindgen(constructor)]
pub fn new() -> Result<EcdhKeyExchange, JsValue> {
let rng = SystemRandom::new();
let private_key = EphemeralPrivateKey::generate(&X25519, &rng)
.map_err(|e| JsValue::from_str(&format!("Key generation failed: {:?}", e)))?;
let public_key = private_key.compute_public_key()
.map_err(|e| JsValue::from_str(&format!("Public key computation failed: {:?}", e)))?;
let public_key_bytes = public_key.as_ref().to_vec();
Ok(EcdhKeyExchange {
private_key: Some(private_key),
public_key: public_key_bytes,
})
}
#[wasm_bindgen]
pub fn get_public_key(&self) -> Vec<u8> {
self.public_key.clone()
}
#[wasm_bindgen]
pub fn compute_shared_secret(&mut self, peer_public_key: &[u8]) -> Result<Vec<u8>, JsValue> {
if let Some(private_key) = self.private_key.take() {
let peer_public_key = PublicKey::from(peer_public_key);
agree_ephemeral(
private_key,
&peer_public_key,
ring::error::Unspecified,
|shared_secret| {
Ok(shared_secret.to_vec())
}
).map_err(|e| JsValue::from_str(&format!("Key agreement failed: {:?}", e)))
} else {
Err(JsValue::from_str("Private key already used"))
}
}
}
// 安全会话管理
#[derive(Serialize, Deserialize)]
#[wasm_bindgen]
pub struct SecureSession {
session_id: String,
encryption_key: Vec<u8>,
mac_key: Vec<u8>,
created_at: f64,
expires_at: f64,
}
#[wasm_bindgen]
impl SecureSession {
#[wasm_bindgen(constructor)]
pub fn new(shared_secret: &[u8], session_duration: f64) -> SecureSession {
use crate::KeyDerivation;
let kdf = KeyDerivation::new();
let now = js_sys::Date::now();
// 生成会话ID
let session_id = format!("{:x}", blake3::hash(&[shared_secret, &now.to_be_bytes()].concat()));
// 派生加密密钥和MAC密钥
let encryption_key = kdf.hkdf_sha256(shared_secret, b"session_encryption", b"", 32);
let mac_key = kdf.hkdf_sha256(shared_secret, b"session_mac", b"", 32);
SecureSession {
session_id,
encryption_key,
mac_key,
created_at: now,
expires_at: now + session_duration * 1000.0,
}
}
#[wasm_bindgen]
pub fn is_valid(&self) -> bool {
js_sys::Date::now() < self.expires_at
}
#[wasm_bindgen]
pub fn get_session_id(&self) -> String {
self.session_id.clone()
}
#[wasm_bindgen]
pub fn encrypt_message(&self, message: &[u8]) -> Result<Vec<u8>, JsValue> {
if !self.is_valid() {
return Err(JsValue::from_str("Session expired"));
}
use crate::AesGcmCipher;
use crate::SecureRandom;
let cipher = AesGcmCipher::new(&self.encryption_key)?;
let rng = SecureRandom::new();
let nonce = rng.generate_bytes(12);
let mut ciphertext = cipher.encrypt(message, &nonce, &[])?;
// 添加nonce到密文前面
let mut result = nonce;
result.append(&mut ciphertext);
Ok(result)
}
#[wasm_bindgen]
pub fn decrypt_message(&self, encrypted_message: &[u8]) -> Result<Vec<u8>, JsValue> {
if !self.is_valid() {
return Err(JsValue::from_str("Session expired"));
}
if encrypted_message.len() < 12 {
return Err(JsValue::from_str("Invalid encrypted message"));
}
use crate::AesGcmCipher;
let cipher = AesGcmCipher::new(&self.encryption_key)?;
let nonce = &encrypted_message[..12];
let ciphertext = &encrypted_message[12..];
cipher.decrypt(ciphertext, nonce, &[])
}
#[wasm_bindgen]
pub fn to_json(&self) -> Result<String, JsValue> {
serde_json::to_string(self)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn from_json(json: &str) -> Result<SecureSession, JsValue> {
serde_json::from_str(json)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
}
// 消息认证码
#[wasm_bindgen]
pub struct MessageAuthenticator {
key: Vec<u8>,
}
#[wasm_bindgen]
impl MessageAuthenticator {
#[wasm_bindgen(constructor)]
pub fn new(key: &[u8]) -> MessageAuthenticator {
MessageAuthenticator {
key: key.to_vec(),
}
}
#[wasm_bindgen]
pub fn authenticate(&self, message: &[u8]) -> Vec<u8> {
use crate::HashFunctions;
let hasher = HashFunctions::new();
hasher.hmac_sha256(&self.key, message)
}
#[wasm_bindgen]
pub fn verify(&self, message: &[u8], mac: &[u8]) -> bool {
let computed_mac = self.authenticate(message);
// 使用常时间比较防止时序攻击
use subtle::ConstantTimeEq;
computed_mac.ct_eq(mac).into()
}
}
安全防护和反调试
// src/protection.rs - 安全防护模块
use wasm_bindgen::prelude::*;
use web_sys::*;
#[wasm_bindgen]
pub struct SecurityMonitor {
start_time: f64,
check_interval: i32,
}
#[wasm_bindgen]
impl SecurityMonitor {
#[wasm_bindgen(constructor)]
pub fn new() -> SecurityMonitor {
SecurityMonitor {
start_time: js_sys::Date::now(),
check_interval: 0,
}
}
#[wasm_bindgen]
pub fn start_monitoring(&mut self, callback: &js_sys::Function) {
let window = web_sys::window().unwrap();
let callback_clone = callback.clone();
let closure = Closure::wrap(Box::new(move || {
let mut threats = Vec::new();
// 检查调试器
if SecurityMonitor::detect_debugger() {
threats.push("debugger_detected");
}
// 检查开发者工具
if SecurityMonitor::detect_devtools() {
threats.push("devtools_detected");
}
// 检查时间异常
if SecurityMonitor::detect_time_manipulation() {
threats.push("time_manipulation");
}
// 检查内存篡改
if SecurityMonitor::detect_memory_tampering() {
threats.push("memory_tampering");
}
if !threats.is_empty() {
let threats_array = js_sys::Array::new();
for threat in threats {
threats_array.push(&JsValue::from_str(threat));
}
let _ = callback_clone.call1(&JsValue::NULL, &threats_array);
}
}) as Box<dyn FnMut()>);
self.check_interval = window
.set_interval_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref(),
1000,
)
.unwrap();
closure.forget();
}
#[wasm_bindgen]
pub fn stop_monitoring(&self) {
if self.check_interval != 0 {
web_sys::window().unwrap().clear_interval_with_handle(self.check_interval);
}
}
fn detect_debugger() -> bool {
// 检查调试器的多种方法
let window = web_sys::window().unwrap();
// 方法1: 检查console对象
let console = window.console();
if let Ok(console_obj) = js_sys::Reflect::get(&console, &"clear".into()) {
if console_obj.is_function() {
// 尝试检测console.clear是否被重写
let clear_str = console_obj.to_string();
if clear_str.as_string().unwrap_or_default().contains("native code") {
return false;
}
}
}
// 方法2: 检查window.outerHeight和window.innerHeight的差异
let outer_height = window.outer_height().unwrap_or(0);
let inner_height = window.inner_height().unwrap_or(0);
if outer_height - inner_height > 200 {
return true;
}
false
}
fn detect_devtools() -> bool {
let window = web_sys::window().unwrap();
// 检查窗口大小变化
let outer_width = window.outer_width().unwrap_or(0);
let inner_width = window.inner_width().unwrap_or(0);
let outer_height = window.outer_height().unwrap_or(0);
let inner_height = window.inner_height().unwrap_or(0);
// 如果内外尺寸差异过大,可能是开发者工具打开
(outer_width - inner_width > 100) || (outer_height - inner_height > 100)
}
fn detect_time_manipulation() -> bool {
let now = js_sys::Date::now();
let performance_now = web_sys::window().unwrap().performance().unwrap().now();
// 检查时间是否异常
(now - performance_now).abs() > 1000.0
}
fn detect_memory_tampering() -> bool {
// 检查WebAssembly内存是否被篡改
let memory = wasm_bindgen::memory();
let buffer = memory.buffer();
// 简单的完整性检查
buffer.byte_length() > 0
}
#[wasm_bindgen]
pub fn obfuscate_string(&self, input: &str) -> String {
// 简单的字符串混淆
input.chars()
.map(|c| {
let code = c as u32;
char::from_u32(code ^ 0xAA).unwrap_or(c)
})
.collect()
}
#[wasm_bindgen]
pub fn deobfuscate_string(&self, input: &str) -> String {
// 解混淆
input.chars()
.map(|c| {
let code = c as u32;
char::from_u32(code ^ 0xAA).unwrap_or(c)
})
.collect()
}
#[wasm_bindgen]
pub fn generate_integrity_hash(&self, data: &[u8]) -> String {
use crate::HashFunctions;
let hasher = HashFunctions::new();
let hash = hasher.blake3(data);
hex::encode(hash)
}
#[wasm_bindgen]
pub fn verify_integrity(&self, data: &[u8], expected_hash: &str) -> bool {
let computed_hash = self.generate_integrity_hash(data);
computed_hash == expected_hash
}
}
// 代码混淆工具
#[wasm_bindgen]
pub struct CodeObfuscator;
#[wasm_bindgen]
impl CodeObfuscator {
#[wasm_bindgen(constructor)]
pub fn new() -> CodeObfuscator {
CodeObfuscator
}
#[wasm_bindgen]
pub fn xor_encrypt(&self, data: &[u8], key: u8) -> Vec<u8> {
data.iter().map(|&b| b ^ key).collect()
}
#[wasm_bindgen]
pub fn xor_decrypt(&self, data: &[u8], key: u8) -> Vec<u8> {
// XOR解密与加密相同
self.xor_encrypt(data, key)
}
#[wasm_bindgen]
pub fn base64_encode(&self, data: &[u8]) -> String {
use base64::{Engine as _, engine::general_purpose};
general_purpose::STANDARD.encode(data)
}
#[wasm_bindgen]
pub fn base64_decode(&self, data: &str) -> Result<Vec<u8>, JsValue> {
use base64::{Engine as _, engine::general_purpose};
general_purpose::STANDARD.decode(data)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
}
实际应用示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Rust-Wasm Crypto Security Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.demo-section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; }
.crypto-container { display: flex; gap: 20px; }
.input-section, .output-section { flex: 1; }
textarea { width: 100%; height: 100px; margin: 5px 0; }
button { padding: 10px; margin: 5px; }
.result { background: #f0f0f0; padding: 10px; margin: 10px 0; }
.security-status { padding: 10px; margin: 10px 0; border-radius: 5px; }
.secure { background: #d4edda; color: #155724; }
.warning { background: #fff3cd; color: #856404; }
.danger { background: #f8d7da; color: #721c24; }
</style>
</head>
<body>
<h1>Rust-Wasm Crypto Security Demo</h1>
<div class="demo-section">
<h2>安全状态监控</h2>
<button onclick="startSecurityMonitoring()">开始监控</button>
<button onclick="stopSecurityMonitoring()">停止监控</button>
<div id="security-status" class="security-status secure">系统安全</div>
</div>
<div class="demo-section">
<h2>对称加密演示</h2>
<div class="crypto-container">
<div class="input-section">
<h3>输入</h3>
<textarea id="symmetric-plaintext" placeholder="输入要加密的文本"></textarea>
<input type="password" id="symmetric-password" placeholder="密码" style="width: 100%;">
<button onclick="symmetricEncrypt()">AES-GCM 加密</button>
<button onclick="symmetricDecrypt()">AES-GCM 解密</button>
</div>
<div class="output-section">
<h3>输出</h3>
<textarea id="symmetric-output" readonly></textarea>
<div id="symmetric-result" class="result"></div>
</div>
</div>
</div>
<div class="demo-section">
<h2>非对称加密演示</h2>
<div class="crypto-container">
<div class="input-section">
<h3>RSA 操作</h3>
<button onclick="generateRsaKeyPair()">生成RSA密钥对</button>
<textarea id="rsa-plaintext" placeholder="输入要加密的文本"></textarea>
<button onclick="rsaEncrypt()">RSA 加密</button>
<button onclick="rsaDecrypt()">RSA 解密</button>
<button onclick="rsaSign()">RSA 签名</button>
<button onclick="rsaVerify()">RSA 验证</button>
</div>
<div class="output-section">
<h3>输出</h3>
<textarea id="rsa-output" readonly></textarea>
<div id="rsa-result" class="result"></div>
</div>
</div>
</div>
<div class="demo-section">
<h2>密钥交换演示</h2>
<div class="crypto-container">
<div class="input-section">
<h3>Alice</h3>
<button onclick="generateAliceKeys()">生成Alice密钥</button>
<div id="alice-public-key" class="result"></div>
<button onclick="aliceComputeSecret()">计算共享密钥</button>
</div>
<div class="output-section">
<h3>Bob</h3>
<button onclick="generateBobKeys()">生成Bob密钥</button>
<div id="bob-public-key" class="result"></div>
<button onclick="bobComputeSecret()">计算共享密钥</button>
</div>
</div>
<div id="key-exchange-result" class="result"></div>
</div>
<div class="demo-section">
<h2>安全会话演示</h2>
<button onclick="createSecureSession()">创建安全会话</button>
<button onclick="sendSecureMessage()">发送加密消息</button>
<div id="session-result" class="result"></div>
<textarea id="session-message" placeholder="输入要发送的消息"></textarea>
<div id="session-output" class="result"></div>
</div>
<script type="module">
import init, {
SecureRandom,
HashFunctions,
KeyDerivation,
AesGcmCipher,
RsaKeyPair,
Ed25519KeyPair,
EcdhKeyExchange,
SecureSession,
SecurityMonitor,
CodeObfuscator
} from './pkg/rust_wasm_crypto.js';
let secureRandom, hashFunctions, keyDerivation;
let rsaKeyPair, ed25519KeyPair;
let aliceKeyExchange, bobKeyExchange;
let aliceSharedSecret, bobSharedSecret;
let secureSession;
let securityMonitor;
let lastEncryptedData = null;
async function run() {
await init();
// 初始化组件
secureRandom = new SecureRandom();
hashFunctions = new HashFunctions();
keyDerivation = new KeyDerivation();
securityMonitor = new SecurityMonitor();
console.log('Crypto security demo loaded!');
}
// 安全监控
window.startSecurityMonitoring = function() {
securityMonitor.start_monitoring((threats) => {
const statusDiv = document.getElementById('security-status');
if (threats.length === 0) {
statusDiv.className = 'security-status secure';
statusDiv.textContent = '系统安全';
} else {
statusDiv.className = 'security-status danger';
statusDiv.textContent = '检测到威胁: ' + Array.from(threats).join(', ');
// 可以在这里添加安全响应措施
console.warn('Security threats detected:', threats);
}
});
};
window.stopSecurityMonitoring = function() {
securityMonitor.stop_monitoring();
document.getElementById('security-status').className = 'security-status warning';
document.getElementById('security-status').textContent = '监控已停止';
};
// 对称加密
window.symmetricEncrypt = function() {
const plaintext = document.getElementById('symmetric-plaintext').value;
const password = document.getElementById('symmetric-password').value;
if (!plaintext || !password) {
alert('请输入文本和密码');
return;
}
try {
// 使用密码派生密钥
const salt = secureRandom.generate_bytes(16);
const key = keyDerivation.pbkdf2_sha256(
new TextEncoder().encode(password),
salt,
100000,
32
);
// 加密
const cipher = new AesGcmCipher(key);
const nonce = secureRandom.generate_bytes(12);
const plaintextBytes = new TextEncoder().encode(plaintext);
const ciphertext = cipher.encrypt(plaintextBytes, nonce, []);
// 组合结果 (salt + nonce + ciphertext)
const result = new Uint8Array(salt.length + nonce.length + ciphertext.length);
result.set(salt, 0);
result.set(nonce, salt.length);
result.set(ciphertext, salt.length + nonce.length);
const base64Result = btoa(String.fromCharCode(...result));
document.getElementById('symmetric-output').value = base64Result;
document.getElementById('symmetric-result').innerHTML =
``;
lastEncryptedData = base64Result;
} catch (error) {
document.getElementById('symmetric-result').innerHTML =
``;
}
};
window.symmetricDecrypt = function() {
const ciphertext = document.getElementById('symmetric-output').value || lastEncryptedData;
const password = document.getElementById('symmetric-password').value;
if (!ciphertext || !password) {
alert('请输入密文和密码');
return;
}
try {
// 解码Base64
const encryptedData = new Uint8Array(
atob(ciphertext).split('').map(c => c.charCodeAt(0))
);
// 提取组件
const salt = encryptedData.slice(0, 16);
const nonce = encryptedData.slice(16, 28);
const ciphertextBytes = encryptedData.slice(28);
// 重新派生密钥
const key = keyDerivation.pbkdf2_sha256(
new TextEncoder().encode(password),
salt,
100000,
32
);
// 解密
const cipher = new AesGcmCipher(key);
const plaintextBytes = cipher.decrypt(ciphertextBytes, nonce, []);
const plaintext = new TextDecoder().decode(new Uint8Array(plaintextBytes));
document.getElementById('symmetric-plaintext').value = plaintext;
document.getElementById('symmetric-result').innerHTML =
``;
} catch (error) {
document.getElementById('symmetric-result').innerHTML =
``;
}
};
// RSA 非对称加密
window.generateRsaKeyPair = function() {
try {
rsaKeyPair = new RsaKeyPair(2048);
document.getElementById('rsa-result').innerHTML =
'<strong>RSA密钥对生成成功</strong><br>密钥长度: 2048位';
} catch (error) {
document.getElementById('rsa-result').innerHTML =
``;
}
};
window.rsaEncrypt = function() {
if (!rsaKeyPair) {
alert('请先生成RSA密钥对');
return;
}
const plaintext = document.getElementById('rsa-plaintext').value;
if (!plaintext) {
alert('请输入要加密的文本');
return;
}
try {
const plaintextBytes = new TextEncoder().encode(plaintext);
const ciphertext = rsaKeyPair.encrypt(plaintextBytes);
const base64Result = btoa(String.fromCharCode(...ciphertext));
document.getElementById('rsa-output').value = base64Result;
document.getElementById('rsa-result').innerHTML =
``;
} catch (error) {
document.getElementById('rsa-result').innerHTML =
``;
}
};
window.rsaDecrypt = function() {
if (!rsaKeyPair) {
alert('请先生成RSA密钥对');
return;
}
const ciphertext = document.getElementById('rsa-output').value;
if (!ciphertext) {
alert('没有密文可解密');
return;
}
try {
const ciphertextBytes = new Uint8Array(
atob(ciphertext).split('').map(c => c.charCodeAt(0))
);
const plaintextBytes = rsaKeyPair.decrypt(ciphertextBytes);
const plaintext = new TextDecoder().decode(new Uint8Array(plaintextBytes));
document.getElementById('rsa-plaintext').value = plaintext;
document.getElementById('rsa-result').innerHTML =
``;
} catch (error) {
document.getElementById('rsa-result').innerHTML =
``;
}
};
window.rsaSign = function() {
if (!rsaKeyPair) {
alert('请先生成RSA密钥对');
return;
}
const message = document.getElementById('rsa-plaintext').value;
if (!message) {
alert('请输入要签名的消息');
return;
}
try {
const messageBytes = new TextEncoder().encode(message);
const signature = rsaKeyPair.sign(messageBytes);
const base64Signature = btoa(String.fromCharCode(...signature));
document.getElementById('rsa-output').value = base64Signature;
document.getElementById('rsa-result').innerHTML =
``;
} catch (error) {
document.getElementById('rsa-result').innerHTML =
``;
}
};
window.rsaVerify = function() {
if (!rsaKeyPair) {
alert('请先生成RSA密钥对');
return;
}
const message = document.getElementById('rsa-plaintext').value;
const signature = document.getElementById('rsa-output').value;
if (!message || !signature) {
alert('请输入消息和签名');
return;
}
try {
const messageBytes = new TextEncoder().encode(message);
const signatureBytes = new Uint8Array(
atob(signature).split('').map(c => c.charCodeAt(0))
);
const isValid = rsaKeyPair.verify(messageBytes, signatureBytes);
document.getElementById('rsa-result').innerHTML =
``;
} catch (error) {
document.getElementById('rsa-result').innerHTML =
``;
}
};
// 密钥交换
window.generateAliceKeys = function() {
try {
aliceKeyExchange = new EcdhKeyExchange();
const publicKey = aliceKeyExchange.get_public_key();
const base64PublicKey = btoa(String.fromCharCode(...publicKey));
document.getElementById('alice-public-key').innerHTML =
``;
} catch (error) {
document.getElementById('alice-public-key').innerHTML =
``;
}
};
window.generateBobKeys = function() {
try {
bobKeyExchange = new EcdhKeyExchange();
const publicKey = bobKeyExchange.get_public_key();
const base64PublicKey = btoa(String.fromCharCode(...publicKey));
document.getElementById('bob-public-key').innerHTML =
``;
} catch (error) {
document.getElementById('bob-public-key').innerHTML =
``;
}
};
window.aliceComputeSecret = function() {
if (!aliceKeyExchange || !bobKeyExchange) {
alert('请先生成双方密钥');
return;
}
try {
const bobPublicKey = bobKeyExchange.get_public_key();
aliceSharedSecret = aliceKeyExchange.compute_shared_secret(bobPublicKey);
document.getElementById('key-exchange-result').innerHTML =
``;
} catch (error) {
document.getElementById('key-exchange-result').innerHTML =
``;
}
};
window.bobComputeSecret = function() {
if (!aliceKeyExchange || !bobKeyExchange) {
alert('请先生成双方密钥');
return;
}
try {
const alicePublicKey = aliceKeyExchange.get_public_key();
bobSharedSecret = bobKeyExchange.compute_shared_secret(alicePublicKey);
const secretsMatch = aliceSharedSecret && bobSharedSecret &&
aliceSharedSecret.every((val, i) => val === bobSharedSecret[i]);
document.getElementById('key-exchange-result').innerHTML +=
``;
} catch (error) {
document.getElementById('key-exchange-result').innerHTML +=
``;
}
};
// 安全会话
window.createSecureSession = function() {
if (!aliceSharedSecret) {
alert('请先完成密钥交换');
return;
}
try {
secureSession = new SecureSession(aliceSharedSecret, 3600); // 1小时
document.getElementById('session-result').innerHTML =
``;
} catch (error) {
document.getElementById('session-result').innerHTML =
``;
}
};
window.sendSecureMessage = function() {
if (!secureSession) {
alert('请先创建安全会话');
return;
}
const message = document.getElementById('session-message').value;
if (!message) {
alert('请输入要发送的消息');
return;
}
try {
const messageBytes = new TextEncoder().encode(message);
const encryptedMessage = secureSession.encrypt_message(messageBytes);
const base64Encrypted = btoa(String.fromCharCode(...encryptedMessage));
// 模拟接收和解密
const decryptedBytes = secureSession.decrypt_message(encryptedMessage);
const decryptedMessage = new TextDecoder().decode(new Uint8Array(decryptedBytes));
document.getElementById('session-output').innerHTML =
`` +
`加密消息: ${base64Encrypted.substring(0, 50)}...<br>` +
`解密消息: ${decryptedMessage}`;
} catch (error) {
document.getElementById('session-output').innerHTML =
``;
}
};
run();
</script>
</body>
</html>
安全部署建议
生产环境配置
// 生产环境安全配置
#[wasm_bindgen]
pub struct ProductionSecurity {
integrity_checks: bool,
obfuscation_enabled: bool,
debug_protection: bool,
}
#[wasm_bindgen]
impl ProductionSecurity {
#[wasm_bindgen(constructor)]
pub fn new() -> ProductionSecurity {
ProductionSecurity {
integrity_checks: true,
obfuscation_enabled: true,
debug_protection: true,
}
}
#[wasm_bindgen]
pub fn validate_environment(&self) -> Result<bool, JsValue> {
// 检查是否在生产环境
let window = web_sys::window().ok_or("No window object")?;
let location = window.location();
let hostname = location.hostname().map_err(|_| "Cannot get hostname")?;
// 检查域名白名单
let allowed_domains = ["yourdomain.com", "www.yourdomain.com"];
if !allowed_domains.iter().any(|&domain| hostname.contains(domain)) {
return Err(JsValue::from_str("Unauthorized domain"));
}
// 检查HTTPS
let protocol = location.protocol().map_err(|_| "Cannot get protocol")?;
if protocol != "https:" {
return Err(JsValue::from_str("HTTPS required"));
}
Ok(true)
}
#[wasm_bindgen]
pub fn enable_tamper_detection(&self) -> Result<(), JsValue> {
// 设置代码完整性检查
let window = web_sys::window().ok_or("No window object")?;
// 检查关键函数是否被篡改
let console = window.console();
let log_function = js_sys::Reflect::get(&console, &"log".into())?;
if !log_function.is_function() {
return Err(JsValue::from_str("Console tampering detected"));
}
Ok(())
}
}
密钥管理最佳实践
// 密钥管理最佳实践
class SecureKeyManager {
constructor() {
this.keyStore = new Map();
this.keyRotationInterval = 24 * 60 * 60 * 1000; // 24小时
}
// 安全存储密钥
storeKey(keyId, keyData, expirationTime) {
const keyInfo = {
data: keyData,
created: Date.now(),
expires: expirationTime || (Date.now() + this.keyRotationInterval),
usage: 0
};
this.keyStore.set(keyId, keyInfo);
// 设置自动清理
setTimeout(() => {
this.deleteKey(keyId);
}, keyInfo.expires - Date.now());
}
// 获取密钥
getKey(keyId) {
const keyInfo = this.keyStore.get(keyId);
if (!keyInfo) {
throw new Error('Key not found');
}
if (Date.now() > keyInfo.expires) {
this.deleteKey(keyId);
throw new Error('Key expired');
}
keyInfo.usage++;
return keyInfo.data;
}
// 安全删除密钥
deleteKey(keyId) {
const keyInfo = this.keyStore.get(keyId);
if (keyInfo) {
// 零化密钥数据
if (keyInfo.data instanceof Uint8Array) {
keyInfo.data.fill(0);
}
this.keyStore.delete(keyId);
}
}
// 密钥轮换
rotateKey(keyId, newKeyData) {
const oldKeyInfo = this.keyStore.get(keyId);
if (oldKeyInfo) {
// 保留旧密钥一段时间以支持解密
const graceKeyId = keyId + '_old_' + Date.now();
this.storeKey(graceKeyId, oldKeyInfo.data, Date.now() + 60000); // 1分钟宽限期
}
this.storeKey(keyId, newKeyData);
}
// 获取密钥统计
getKeyStats() {
const stats = {
totalKeys: this.keyStore.size,
expiredKeys: 0,
activeKeys: 0
};
const now = Date.now();
for (const [keyId, keyInfo] of this.keyStore) {
if (now > keyInfo.expires) {
stats.expiredKeys++;
} else {
stats.activeKeys++;
}
}
return stats;
}
}
总结
Rust-Wasm加解密安全防护方案的核心要点:
🎯 加密技术栈
- 对称加密:AES-GCM、ChaCha20-Poly1305高性能加密
- 非对称加密:RSA、Ed25519密钥对和数字签名
- 密钥派生:PBKDF2、Argon2、HKDF安全密钥生成
- 哈希函数:SHA-2、SHA-3、BLAKE3多种哈希算法
✅ 安全协议
- ECDH密钥交换协议
- 安全会话管理
- 消息认证码(MAC)
- 完整性验证机制
🚀 防护策略
- 反调试和反篡改检测
- 代码混淆和字符串保护
- 内存安全管理
- 实时安全监控
💡 最佳实践
- 零化敏感数据
- 常时间比较防时序攻击
- 安全随机数生成
- 多层防护机制
🔒 生产部署
- 环境验证和域名检查
- HTTPS强制要求
- 密钥轮换和管理
- 完整性监控
构建安全可靠的Web加密系统,保护用户数据安全!
安全是一个持续的过程,需要在设计、实现和部署的各个阶段都保持高度的安全意识和严格的安全标准。