vue3 + WebAssembly 前端rsa加解密

148 阅读1分钟

rust

1. 安装 Rust工具链
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
2. 安装wasm-pack
cargo install wasm-pack
3. cargo.toml可添加wasm-bindgen (方便web打印console)
crate-type = ["cdylib"]
path = "src/lib.rs" # 引用的文件

[dependencies]
wasm-bindgen = "0.2.83"
rand = "0.8.5"
base64 = "0.22.1"
rsa = "0.9.8"
blowfish = "0.8.0"
block-modes = "0.8.0"
getrandom = { version = "0.2", features = ["js"] }
web-sys = { version = "0.3", features = ["Window", "Document"] }
4. lib.rs
use base64::{Engine as _, engine::general_purpose};
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cbc};
use blowfish::Blowfish;
use rand::{Rng, distributions::Alphanumeric};
use rsa::pkcs8::DecodePrivateKey;
use rsa::pkcs8::DecodePublicKey;
use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};

static PRIKEY: &str = "-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----";
...
-----END PUBLIC KEY-----";
static IV: &[u8; 8] = b"12345678";
type BlowfishCbc = Cbc<Blowfish, Pkcs7>;

fn main() {
    let rng = rand::thread_rng();
    let key_str: String = rng
        .sample_iter(Alphanumeric)
        .take(8)
        .map(char::from)
        .collect();
    println!("origin key is: {key_str}");
    let data = encrypt_content(String::from("hello world"));
    println!("encrypt result is: {data}");
    let ret2 = decrypt_content(data);
    println!("result is: {}", ret2);
}

fn decrypt_content(body: String) -> String {
    let data = general_purpose::STANDARD
        .decode(body)
        .expect("decode error");
    let head = &data[..256];
    let body = &data[256..];
    let private_key = RsaPrivateKey::from_pkcs8_pem(PRIKEY).expect("decrypt key init error");
    let key = private_key
        .decrypt(Pkcs1v15Encrypt, head)
        .expect("decrypt key error");
    let rest = BlowfishCbc::new_from_slices(key.as_slice(), IV)
        .expect("decrypt body init error")
        .decrypt_vec(body)
        .expect("decrypt body error");
    String::from_utf8(rest).unwrap_or_else(|_e| String::from("bad data"))
}

fn encrypt_content(data: String) -> String {
    let rng = rand::thread_rng();
    let key: String = rng
        .sample_iter(Alphanumeric)
        .take(8)
        .map(char::from)
        .collect();
    let public_key = RsaPublicKey::from_public_key_pem(PUBKEY).expect("encrypt key init error");
    let mut rng = rand::thread_rng();
    //加密的header
    let mut body = public_key
        .encrypt(&mut rng, Pkcs1v15Encrypt, key.as_bytes())
        .expect("encrypt body init error");
    //加密剩下的
    let rest = BlowfishCbc::new_from_slices(key.as_bytes(), IV)
        .expect("encrypt body error")
        .encrypt_vec(data.as_bytes());
    body.extend(rest);
    general_purpose::STANDARD.encode(body)
}

vue3+vite版本

1. 安装支持wasm插件
yarn add vite-plugin-wasm vite-plugin-top-level-await
2. 配置vite.config.ts
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

// 添加插件
plugins: [vue(), wasm(), topLevelAwait()]
3. 安装依赖

将打包后的pkg文件移入vue项目中,完成之后,我们可以在node_modules里面关联一下vue项目下面的pkg包。

    yarn add pkg路径

若想自动编译同步更新node_modules里面内容,我们在package.json里面写一个执行命令。

"scripts": { "wasm":  yarn add  pkg路径" },

4. 封装 Wasm 工具类

import init, { encrypt_content, decrypt_content } from '../../pkg/bism';

// 初始化
(async function loadAndRunGoWasm() {
  await init();
})();

export function encrypt(data: string) {
  return encrypt_content(data);
}

export function decrypt(data: string) {
  return decrypt_content(data);
}

import { encrypt, decrypt } from './wasmHelper';

const params = encrypt(JSON.stringify(加密参数));
console.log(`加密, ${params}`);
const result = decrypt(params);
console.log(`解密, ${result}`);