You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 20:05:53