Created
July 23, 2025 01:37
-
-
Save beyoung/a8ce583df6e0381b89232d2a6b04abaf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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