Skip to content

Instantly share code, notes, and snippets.

@beyoung
Created July 23, 2025 01:37
Show Gist options
  • Select an option

  • Save beyoung/a8ce583df6e0381b89232d2a6b04abaf to your computer and use it in GitHub Desktop.

Select an option

Save beyoung/a8ce583df6e0381b89232d2a6b04abaf to your computer and use it in GitHub Desktop.
// AES加密解密模块 - 浏览器版本
// 使用Web Crypto API实现
class AESCoder {
constructor(password = "", salt = "") {
this.password = password;
this.salt = salt;
this.key = null;
this.init();
}
// 初始化,生成AES密钥
async init() {
try {
// 将密码和盐组合
const passwordData = `${this.password}:${this.salt}`;
// 使用PBKDF2生成密钥
const passwordBuffer = new TextEncoder().encode(passwordData);
const saltBuffer = new TextEncoder().encode(this.salt);
// 导入密码作为密钥材料
const passwordKey = await crypto.subtle.importKey(
'raw',
passwordBuffer,
{ name: 'PBKDF2' },
false,
['deriveBits', 'deriveKey']
);
// 使用PBKDF2派生AES密钥
this.key = await crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: saltBuffer,
iterations: 100000,
hash: 'SHA-256'
},
passwordKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
} catch (error) {
console.error('初始化AES密钥失败:', error);
throw error;
}
}
// AES加密
async aesEncode(message) {
try {
if (!this.key) {
await this.init();
}
// 生成随机nonce
const nonce = crypto.getRandomValues(new Uint8Array(12));
// 编码消息
const messageBuffer = new TextEncoder().encode(message);
// 加密
const encrypted = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: nonce
},
this.key,
messageBuffer
);
// 分离密文和认证标签
const ciphertext = encrypted.slice(0, -16);
const tag = encrypted.slice(-16);
return {
nonce: this.arrayBufferToBase64(nonce),
cipher: this.arrayBufferToBase64(ciphertext),
tag: this.arrayBufferToBase64(tag)
};
} catch (error) {
console.error('AES加密失败:', error);
throw error;
}
}
// AES解密
async aesDecode(message) {
try {
if (!this.key) {
await this.init();
}
// 解码Base64数据
const nonce = this.base64ToArrayBuffer(message.nonce);
const ciphertext = this.base64ToArrayBuffer(message.cipher);
const tag = this.base64ToArrayBuffer(message.tag);
// 组合密文和认证标签
const encrypted = new Uint8Array(ciphertext.byteLength + tag.byteLength);
encrypted.set(new Uint8Array(ciphertext), 0);
encrypted.set(new Uint8Array(tag), ciphertext.byteLength);
// 解密
const decrypted = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: nonce
},
this.key,
encrypted
);
// 转换为字符串
return new TextDecoder().decode(decrypted);
} catch (error) {
console.error('AES解密失败:', error, message);
return null;
}
}
// ArrayBuffer转Base64
arrayBufferToBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
// Base64转ArrayBuffer
base64ToArrayBuffer(base64) {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
}
// 创建全局实例
const aesCoder = new AESCoder();
// 导出函数(兼容原Python接口)
async function aes_encode(message) {
return await aesCoder.aesEncode(message);
}
async function aes_decode(message) {
return await aesCoder.aesDecode(message);
}
// 如果是在浏览器环境中,将函数挂载到window对象
if (typeof window !== 'undefined') {
window.AESCoder = AESCoder;
window.aes_encode = aes_encode;
window.aes_decode = aes_decode;
window.aesCoder = aesCoder;
}
// 如果是在Node.js环境中,导出模块
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
AESCoder,
aes_encode,
aes_decode,
aesCoder
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment