RSA-OAEP加密超过190字节数据报错,大数据加密的正确方案是什么?
报错原因
RSA 非对称加密本身的设计定位就是加密小长度数据,不存在加密任意长度明文的实现方案,你遇到的长度限制是标准特性:
2048位的RSA密钥配合RSA-OAEP、SHA-256参数时,最大可加密的明文长度为 2048/8 - 2*32 - 2 = 190 字节,超过这个长度就会抛出加密异常。
大体积数据加密的标准实现方案
业内通用的做法是混合加密,结合非对称加密的安全性和对称加密的高性能、无长度限制特性,流程如下:
- 生成随机的对称加密密钥,推荐使用
AES-GCM算法(自带完整性校验,安全等级更高) - 用生成的AES密钥加密需要处理的大体积明文
- 用RSA公钥加密这个AES对称密钥
- 最终传输/存储的加密内容由三部分拼接组成:RSA加密后的AES密钥、AES加密使用的随机IV、AES加密得到的密文
解密时反向操作即可:先拆分得到三个部分,用RSA私钥解密得到AES密钥,再用AES密钥+IV解密密文得到原始数据。
完整实现示例
// 生成RSA密钥对,和原有逻辑一致 async function generateRsaKeyPair() { return crypto.subtle.generateKey({ name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256", }, true, ["encrypt", "decrypt"]) } // 加密任意长度数据 async function encryptData(publicKey, rawData) { // 1. 生成AES-GCM密钥 const aesKey = await crypto.subtle.generateKey( { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ) // 2. 生成随机IV,AES-GCM推荐用12字节IV const iv = crypto.getRandomValues(new Uint8Array(12)) // 3. 用AES加密原始数据 const encryptedContent = await crypto.subtle.encrypt( { name: "AES-GCM", iv: iv }, aesKey, rawData ) // 4. 导出AES密钥的原始二进制格式 const rawAesKey = await crypto.subtle.exportKey("raw", aesKey) // 5. 用RSA公钥加密AES密钥 const encryptedAesKey = await crypto.subtle.encrypt( { name: "RSA-OAEP" }, publicKey, rawAesKey ) // 6. 拼接所有结果返回:加密的AES密钥(256字节) + IV(12字节) + 加密内容 const result = new Uint8Array(encryptedAesKey.byteLength + iv.byteLength + encryptedContent.byteLength) result.set(new Uint8Array(encryptedAesKey), 0) result.set(iv, encryptedAesKey.byteLength) result.set(new Uint8Array(encryptedContent), encryptedAesKey.byteLength + iv.byteLength) return result } // 解密数据 async function decryptData(privateKey, encryptedData) { // 拆分数据,长度对应加密时的规则 const encryptedAesKey = encryptedData.slice(0, 256) const iv = encryptedData.slice(256, 256 + 12) const encryptedContent = encryptedData.slice(256 + 12) // 1. RSA解密得到AES密钥 const rawAesKey = await crypto.subtle.decrypt( { name: "RSA-OAEP" }, privateKey, encryptedAesKey ) // 2. 导入AES密钥 const aesKey = await crypto.subtle.importKey( "raw", rawAesKey, { name: "AES-GCM", length: 256 }, true, ["encrypt", "decrypt"] ) // 3. AES解密得到原始数据 const rawData = await crypto.subtle.decrypt( { name: "AES-GCM", iv: iv }, aesKey, encryptedContent ) return rawData } // 测试用例 async function test(size) { const keyPair = await generateRsaKeyPair() const rawData = new Uint8Array(size) try { const encrypted = await encryptData(keyPair.publicKey, rawData) const decrypted = await decryptData(keyPair.privateKey, encrypted) console.log(`${size}字节加密成功,解密后长度匹配:${decrypted.byteLength === size}`) } catch (ex) { console.log(`${size}字节加密失败:${ex.constructor.name} ${ex}`) } } test(191) // 成功 test(500) // 成功 test(1024 * 1024) // 1MB数据也可正常加密
内容的提问来源于stack exchange,提问作者Qwertiy
相关产品推荐
相关产品推荐

