SubtleCrypto RSA-OAEP解密报DOMException:数据不符合要求求助
RSA-OAEP解密抛出
DOMException: Data provided to an operation does not meet requirements问题排查 我参考StackOverflow线程实现SubtleCrypto的RSA加解密功能,用在线工具生成密钥(仅移除私钥中的“RSA”标签),对原代码做了以下修改:
- 调整函数顺序
- 替换为生成的密钥
- 为加解密函数添加输入参数与返回值
- 配置SHA-256哈希及1024位密钥长度
- 新增chainTest函数做加解密闭环测试
目前加密可生成输出,但解密时始终抛出DOMException: Data provided to an operation does not meet requirements错误,无法定位具体问题,代码如下:
const publicKey = `-----BEGIN PUBLIC KEY----- MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgbY/2cGDeAsBUtSAbNx9fV3p1V i8lES4vW576Hafuf4ACUVwYimYF+WMw680f1uSMYFPZiPgG4ifZrQI510Qd60mnZ hPyXRwdBXRex8T5gZVGtu3farONvYAH9xy182rQCfMhyYJw8WD9s1h0NA0i3yGlK zOuPrBqmoZ9i45hbAgMBAAE= -----END PUBLIC KEY-----`; const privateKey = `-----BEGIN PRIVATE KEY----- MIICWwIBAAKBgHgbY/2cGDeAsBUtSAbNx9fV3p1Vi8lES4vW576Hafuf4ACUVwYi mYF+WMw680f1uSMYFPZiPgG4ifZrQI510Qd60mnZhPyXRwdBXRex8T5gZVGtu3fa rONvYAH9xy182rQCfMhyYJw8WD9s1h0NA0i3yGlKzOuPrBqmoZ9i45hbAgMBAAEC gYBdSEi8rANS+CvKBsUuI5zW0VB4ufw7cUOLdHnAzMNPnrgHOy7roOvAWzT0ScSx WvNTglylj1/BTmY4cMxO2MpjCC5MAtwM9QOaU405WatHBgCKiRKNLHCGA2KoQlJ8 let5HLYCUf+1nRtGDVrKVRBpKn2uT9ukL2SCwbo3gz59IQJBALbsjHC35srxL8rT rnhk9O4j8zJ+uIUIobY/duODP1JOP6+gtjCYPzAivLy6+6an3M2DSjAN34XPCsEk wrwPof0CQQCoFpwKYznNx+73CCtT4Q9mWQZlr4IzjawRFEDNZrg6ZrfWnbX0wt+M Wv6M74leqIWr6Q7cknxXZPKSzGG0N2c3AkEAk9EdP+zr/GzgIPfj0fhLELFOUiyi sSYjf2FTklA5+CmxPxwQfb5ZuW0otR4oQyj8vbntVl2vlbKUTzWEg3HihQJARcul nZaYMP99FayZuSmx6FC9HEolaVzBfxIG2oN1qiJu4bn5DRpCExjRrBnm05xsbPbI SgS1huCO7S/avidnPwJAK1RFqYuawbMyl17eHFSnWvXHoUMle0Ywd1RT+dx8EI12 TJZDRWJirUZzTg+mN6Kryh3YkdtgYOvRDckFg43dug== -----END PRIVATE KEY-----`; async function chainTest(plaintext){ var d = await importPublicKeyAndEncrypt(plaintext); console.log("d -> " + d); var e = await importPrivateKeyAndDecrypt(d); console.log("e -> " + e); } /********** import/export executable functions *******/ async function importPublicKeyAndEncrypt(plaintext) { try { const pub = await importPublicKey(publicKey); const encrypted = await encryptRSA(pub, new TextEncoder().encode(plaintext)); const encryptedBase64 = window.btoa(ab2str(encrypted)); return encryptedBase64; } catch(error) { console.log(error); } } async function importPrivateKeyAndDecrypt(ciphertextB64) { try { const priv = await importPrivateKey(privateKey); const decrypted = await decryptRSA(priv, str2ab(window.atob(ciphertextB64))); return decrypted; } catch(error) { console.log(error); } } /******* encrypt/decrypt **********/ async function encryptRSA(key, plaintext) { let encrypted = await window.crypto.subtle.encrypt( { name: "RSA-OAEP", hash: "SHA-256", length:1024, }, key, plaintext ); return encrypted; } async function decryptRSA(key, ciphertext) { let decrypted = await window.crypto.subtle.decrypt( { name: "RSA-OAEP", hash: "SHA-256", length:1024, }, key, ciphertext ); return new TextDecoder().decode(decrypted); } /******** imports *************/ async function importPrivateKey(pkcs8Pem) { return await window.crypto.subtle.importKey( "pkcs8", getPkcs8Der(pkcs8Pem), { name: "RSA-OAEP", hash: "SHA-256", length:1024, }, true, ["decrypt"] ); } async function importPublicKey(spkiPem) { return await window.crypto.subtle.importKey( "spki", getSpkiDer(spkiPem), { name: "RSA-OAEP", hash: "SHA-256", length:1024, }, true, ["encrypt"] ); } /******** filters *************/ function getSpkiDer(spkiPem){ const pemHeader = "-----BEGIN PUBLIC KEY-----"; const pemFooter = "-----END PUBLIC KEY-----"; var pemContents = spkiPem.substring(pemHeader.length, spkiPem.length - pemFooter.length); console.log(pemContents); var binaryDerString = window.atob(pemContents); return str2ab(binaryDerString); } function getPkcs8Der(pkcs8Pem){ const pemHeader = "-----BEGIN PRIVATE KEY-----"; const pemFooter = "-----END PRIVATE KEY-----"; var pemContents = pkcs8Pem.substring(pemHeader.length, pkcs8Pem.length - pemFooter.length); var binaryDerString = window.atob(pemContents); return str2ab(binaryDerString); } /********* helpers ***********/ function ab2str(buf) { return String.fromCharCode.apply(null, new Uint8Array(buf)); } function str2ab(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; }
问题修复方案
1. 移除算法参数中多余的length属性
RSA-OAEP的算法配置对象没有length属性,密钥长度会从导入的密钥本身自动读取。你在加密、解密、密钥导入函数里添加的length:1024会导致SubtleCrypto解析参数失败,这是解密报错的核心原因。
2. 正确清理PEM密钥内容
原代码提取PEM内容时没有去除换行符和空格,这些非Base64字符会导致atob解码出错误的DER格式数据,进而导致密钥导入异常。需要过滤掉所有非Base64字符。
3. 修复ArrayBuffer与字符串转换的编码问题
ab2str函数直接用String.fromCharCode.apply处理Uint8Array,当字节值大于127时会出现编码错误,改用展开运算符可以避免这个问题。
修复后的完整代码
const publicKey = `-----BEGIN PUBLIC KEY----- MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgbY/2cGDeAsBUtSAbNx9fV3p1V i8lES4vW576Hafuf4ACUVwYimYF+WMw680f1uSMYFPZiPgG4ifZrQI510Qd60mnZ hPyXRwdBXRex8T5gZVGtu3farONvYAH9xy182rQCfMhyYJw8WD9s1h0NA0i3yGlK zOuPrBqmoZ9i45hbAgMBAAE= -----END PUBLIC KEY-----`; const privateKey = `-----BEGIN PRIVATE KEY----- MIICWwIBAAKBgHgbY/2cGDeAsBUtSAbNx9fV3p1Vi8lES4vW576Hafuf4ACUVwYi mYF+WMw680f1uSMYFPZiPgG4ifZrQI510Qd60mnZhPyXRwdBXRex8T5gZVGtu3fa rONvYAH9xy182rQCfMhyYJw8WD9s1h0NA0i3yGlKzOuPrBqmoZ9i45hbAgMBAAEC gYBdSEi8rANS+CvKBsUuI5zW0VB4ufw7cUOLdHnAzMNPnrgHOy7roOvAWzT0ScSx WvNTglylj1/BTmY4cMxO2MpjCC5MAtwM9QOaU405WatHBgCKiRKNLHCGA2KoQlJ8 let5HLYCUf+1nRtGDVrKVRBpKn2uT9ukL2SCwbo3gz59IQJBALbsjHC35srxL8rT rnhk9O4j8zJ+uIUIobY/duODP1JOP6+gtjCYPzAivLy6+6an3M2DSjAN34XPCsEk wrwPof0CQQCoFpwKYznNx+73CCtT4Q9mWQZlr4IzjawRFEDNZrg6ZrfWnbX0wt+M Wv6M74leqIWr6Q7cknxXZPKSzGG0N2c3AkEAk9EdP+zr/GzgIPfj0fhLELFOUiyi sSYjf2FTklA5+CmxPxwQfb5ZuW0otR4oQyj8vbntVl2vlbKUTzWEg3HihQJARcul nZaYMP99FayZuSmx6FC9HEolaVzBfxIG2oN1qiJu4bn5DRpCExjRrBnm05xsbPbI SgS1huCO7S/avidnPwJAK1RFqYuawbMyl17eHFSnWvXHoUMle0Ywd1RT+dx8EI12 TJZDRWJirUZzTg+mN6Kryh3YkdtgYOvRDckFg43dug== -----END PRIVATE KEY-----`; async function chainTest(plaintext){ var d = await importPublicKeyAndEncrypt(plaintext); console.log("d -> " + d); var e = await importPrivateKeyAndDecrypt(d); console.log("e -> " + e); } /********** import/export executable functions *******/ async function importPublicKeyAndEncrypt(plaintext) { try { const pub = await importPublicKey(publicKey); const encrypted = await encryptRSA(pub, new TextEncoder().encode(plaintext)); const encryptedBase64 = window.btoa(String.fromCharCode(...new Uint8Array(encrypted))); return encryptedBase64; } catch(error) { console.log(error); } } async function importPrivateKeyAndDecrypt(ciphertextB64) { try { const priv = await importPrivateKey(privateKey); const ciphertextBytes = new Uint8Array(window.atob(ciphertextB64).split('').map(c => c.charCodeAt(0))); const decrypted = await decryptRSA(priv, ciphertextBytes.buffer); return decrypted; } catch(error) { console.log(error); } } /******* encrypt/decrypt **********/ async function encryptRSA(key, plaintext) { let encrypted = await window.crypto.subtle.encrypt( { name: "RSA-OAEP", hash: "SHA-256", }, key, plaintext ); return encrypted; } async function decryptRSA(key, ciphertext) { let decrypted = await window.crypto.subtle.decrypt( { name: "RSA-OAEP", hash: "SHA-256", }, key, ciphertext ); return new TextDecoder().decode(decrypted); } /******** imports *************/ async function importPrivateKey(pkcs8Pem) { return await window.crypto.subtle.importKey( "pkcs8", getPkcs8Der(pkcs8Pem), { name: "RSA-OAEP", hash: "SHA-256", }, true, ["decrypt"] ); } async function importPublicKey(spkiPem) { return await window.crypto.subtle.importKey( "spki", getSpkiDer(spkiPem), { name: "RSA-OAEP", hash: "SHA-256", }, true, ["encrypt"] ); } /******** filters *************/ function getSpkiDer(spkiPem){ const pemHeader = "-----BEGIN PUBLIC KEY-----"; const pemFooter = "-----END PUBLIC KEY-----"; var pemContents = spkiPem.substring(pemHeader.length, spkiPem.length - pemFooter.length); // 过滤所有非Base64字符 pemContents = pemContents.replace(/[\r\n\s]/g, ''); var binaryDerString = window.atob(pemContents); return str2ab(binaryDerString); } function getPkcs8Der(pkcs8Pem){ const pemHeader = "-----BEGIN PRIVATE KEY-----"; const pemFooter = "-----END PRIVATE KEY-----"; var pemContents = pkcs8Pem.substring(pemHeader.length, pkcs8Pem.length - pemFooter.length); // 过滤所有非Base64字符 pemContents = pemContents.replace(/[\r\n\s]/g, ''); var binaryDerString = window.atob(pemContents); return str2ab(binaryDerString); } /********* helpers ***********/ function str2ab(str) { const buf = new ArrayBuffer(str.length); const bufView = new Uint8Array(buf); for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; }
验证说明
调用chainTest("test message")后,控制台会输出加密后的Base64字符串,以及解密还原的原始明文,加解密闭环测试可正常通过。
内容的提问来源于stack exchange,提问作者Zorak
相关产品推荐
相关产品推荐

