WebCrypto API实战:前端加密与安全通信完全指南
WebCrypto API简介 WebCrypto API是现代浏览器提供的原生加密API,提供了安全的加密操作能力。与第三方库相比,它有以下优势: 浏览器原生支持,无需额外依赖 使用系统级加密库,性能更优 安全的密钥管理,密钥不暴露给JavaScript 同步和异步API支持 对称加密(AES) 生成密钥 1 2 3 4 5 6 7 8 9 10 11 12 // 生成AES密钥 async function generateAesKey() { const key = await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'] ) return key } 加密数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 async function encryptData(data, key) { const encoder = new TextEncoder() const iv = crypto.getRandomValues(new Uint8Array(12)) const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, key, encoder.encode(data) ) return { ciphertext: new Uint8Array(encrypted), iv: iv } } 解密数据 1 2 3 4 5 6 7 8 9 10 11 12 13 async function decryptData(encryptedData, key, iv) { const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, key, encryptedData ) const decoder = new TextDecoder() return decoder.decode(decrypted) } 非对称加密(RSA) 生成密钥对 1 2 3 4 5 6 7 8 9 10 11 12 13 14 async function generateRsaKeyPair() { const keyPair = await crypto.subtle.generateKey( { name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, true, ['encrypt', 'decrypt'] ) return keyPair } 导出和导入密钥 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 导出公钥 async function exportPublicKey(key) { const exported = await crypto.subtle.exportKey( 'spki', key ) return new Uint8Array(exported) } // 导入公钥 async function importPublicKey(keyData) { return await crypto.subtle.importKey( 'spki', keyData, { name: 'RSA-OAEP', hash: 'SHA-256' }, true, ['encrypt'] ) } 哈希算法 计算哈希 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function hashData(data, algorithm = 'SHA-256') { const encoder = new TextEncoder() const dataBuffer = encoder.encode(data) const hashBuffer = await crypto.subtle.digest( algorithm, dataBuffer ) const hashArray = Array.from(new Uint8Array(hashBuffer)) return hashArray.map(b => b.toString(16).padStart(2, '0')).join('') } // 使用 const hash = await hashData('Hello, WebCrypto!', 'SHA-256') console.log(hash) // 64位十六进制字符串 HMAC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 async function hmac(key, message) { const encoder = new TextEncoder() const cryptoKey = await crypto.subtle.importKey( 'raw', encoder.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'] ) const signature = await crypto.subtle.sign( 'HMAC', cryptoKey, encoder.encode(message) ) const hashArray = Array.from(new Uint8Array(signature)) return hashArray.map(b => b.toString(16).padStart(2, '0')).join('') } 数字签名 生成签名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 async function signData(data, privateKey) { const encoder = new TextEncoder() const signature = await crypto.subtle.sign( { name: 'RSA-PSS', saltLength: 32 }, privateKey, encoder.encode(data) ) return new Uint8Array(signature) } 验证签名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async function verifySignature(data, signature, publicKey) { const encoder = new TextEncoder() const result = await crypto.subtle.verify( { name: 'RSA-PSS', saltLength: 32 }, publicKey, signature, encoder.encode(data) ) return result } 密钥派生(PBKDF2) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 async function deriveKey(password, salt) { const encoder = new TextEncoder() const keyMaterial = await crypto.subtle.importKey( 'raw', encoder.encode(password), 'PBKDF2', false, ['deriveKey'] ) const key = await crypto.subtle.deriveKey( { name: 'PBKDF2', salt: encoder.encode(salt), iterations: 100000, hash: 'SHA-256' }, keyMaterial, { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'] ) return key } 实战案例 端到端加密聊天 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 class E2EChat { constructor() { this.keyPair = null this.peerKeys = new Map() } async init() { // 生成密钥对 this.keyPair = await crypto.subtle.generateKey( { name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, true, ['encrypt', 'decrypt'] ) } async getPublicKey() { const exported = await crypto.subtle.exportKey('spki', this.keyPair.publicKey) return btoa(String.fromCharCode(...new Uint8Array(exported))) } async addPeer(id, publicKeyData) { const binaryString = atob(publicKeyData) const bytes = new Uint8Array(binaryString.length) for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i) } const publicKey = await crypto.subtle.importKey( 'spki', bytes, { name: 'RSA-OAEP', hash: 'SHA-256' }, true, ['encrypt'] ) this.peerKeys.set(id, publicKey) } async sendMessage(peerId, message) { const publicKey = this.peerKeys.get(peerId) if (!publicKey) throw new Error('Peer not found') // 生成会话密钥 const sessionKey = await this.generateSessionKey() // 加密消息 const encryptedMessage = await this.encryptMessage(message, sessionKey) // 加密会话密钥 const encryptedKey = await this.encryptSessionKey(sessionKey, publicKey) return { message: encryptedMessage, key: encryptedKey } } async receiveMessage(data) { // 解密会话密钥 const sessionKey = await this.decryptSessionKey(data.key) // 解密消息 return await this.decryptMessage(data.message, sessionKey) } async generateSessionKey() { return await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt'] ) } async encryptMessage(message, key) { const encoder = new TextEncoder() const iv = crypto.getRandomValues(new Uint8Array(12)) const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: iv }, key, encoder.encode(message) ) return { data: btoa(String.fromCharCode(...new Uint8Array(encrypted))), iv: btoa(String.fromCharCode(...iv)) } } async decryptMessage(data, key) { const ciphertext = Uint8Array.from(atob(data.data), c => c.charCodeAt(0)) const iv = Uint8Array.from(atob(data.iv), c => c.charCodeAt(0)) const decrypted = await crypto.subtle.decrypt( { name: 'AES-GCM', iv: iv }, key, ciphertext ) const decoder = new TextDecoder() return decoder.decode(decrypted) } } 安全最佳实践 密钥管理 ...