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

使用crypto.subtle.decrypt解密OpenSSL生成的密文时出现SyntaxError

问题:使用Web Crypto API解密OpenSSL生成的密文报错

我尝试用crypto.subtle.decrypt解密OpenSSL生成的密文,一直碰到这个错误:

SyntaxError: An invalid or illegal string was specified

这个密文能通过在线RSA工具和OpenSSL正常解密,我试了StackOverflow上好几个示例,但这些示例里的密文解密也失败了。我怀疑加密时漏了某些参数,但不确定具体是哪一个。

密钥生成命令

我用这些命令生成密钥:

$ openssl genpkey -out private_key.pem -algorithm RSA -pkeyopt rsa_keygen_bits:1024
$ openssl rsa -pubout -in private_key.pem -out public_key.pem
$ cat public_key.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7P6BUs5/P9NbyztqsXvhYHFdX
sQQ5rw6R80MpH6KIgwZohpTLIuZIUjoG/9G+Artv1FHL1uvOPfqlUsj2HtVbM8Cz
E52tB/z/kIyLA6GjKYt+Ok2ZUgWMuHXFBICANaLd93Vm1RNZTcj4k5B/3UT+zZ8j
i30YJjPoLJrtx8VDTQIDAQAB
-----END PUBLIC KEY-----

加密命令

我用以下命令加密明文:

$ echo "Rohit Das" | openssl pkeyutl -encrypt -pubin -inkey public_key.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -out pass_1.enc
$ cat pass_1.enc | base64
Bubb/cepSt/BZAoPjjsRtmy0Y/50cwcP6J0GvKavFEfLYzAkMi/JbueGILIWnllnENZM0w133JoV
pPPT9B21hD2p8z2CzuOmYV4BnVV97i0oS0xawJPWdUwc6Vp+JaN9TrEX98FwCtQZHKtViyA8+Bnm
u2LBKl5N/QipKIX+Dnc=

用于解密的JS代码

function str2ab(str) {
    const bufView = new Uint8Array(str.length);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
      bufView[i] = str.charCodeAt(i);
    }
    return bufView.buffer;
}

function decrypto() {
    let pvtPem = `-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALs/oFSzn8/01vLO
2qxe+FgcV1exBDmvDpHzQykfooiDBmiGlMsi5khSOgb/0b4Cu2/UUcvW6849+qVS
yPYe1VszwLMTna0H/P+QjIsDoaMpi346TZlSBYy4dcUEgIA1ot33dWbVE1lNyPiT
kH/dRP7NnyOLfRgmM+gsmu3HxUNNAgMBAAECgYEApO8ri9BYwbWZrHCWX2Sb/gig
ysZKwYC4JckP7GZIJVS8TU/WOoQ4MZX0NPwbRPJlJeDwV8utE5K2d+9OwrRwGw7n
ZirNolvYSnBeZ8pKRwseQA2fzfCMubZcttjpyb1DfyMXwQj6oCGxuFXPLUWT8/1/
9PkPAWa8uOZhinD/fwUCQQDcxcdCn9rAFLOn1g3EJzSD56GGu6RIrkx8kdBofTYr
nwYnriiX8a//hlRg4BiOv1FdoTHFJi3QAOobX8tiUBK7AkEA2SBxS2TDOj3HJByI
32D0HY1a/O3VBEF4mkRi60HtDYRxL7fmCxebgzFytGxIQr4h+VxFqlABDoDd5vd3
cSS1lwJARm3FumhalYpFIda0f43uP+Il8mBr8U/BUMAHlz3SiSnrAb+abZaJid+l
jV4QF4HLCC6DPRyH4uJXzLHLpSpcPwJARYVVwUYqHGPbd3yLdrqcbznrgEDGi+5K
p1puMdWSCVn2w8imJ7cPXBphF9Pz7yrhxe39gGLNc89fPazO2bNfUQJBALBdr/G3
KYecLkwJeR0okQ8biSr1mAC+VhqlF9JW4pDiw4jrud6R+M4+WjU5nGjxPCfhbEHA
PCahv9z7njXL2qA=
-----END PRIVATE KEY-----`;
    const pemHeader = "-----BEGIN PRIVATE KEY-----";
    const pemFooter = "-----END PRIVATE KEY-----";
    let pvtDer = pvtPem.substring(pemHeader.length, pvtPem.length - pemFooter.length);
    let binaryDer = str2ab(window.atob(pvtDer));
    console.log("binaryDer " + binaryDer);
    window.crypto.subtle.importKey(
        "pkcs8",
        binaryDer,
        {
            name: "RSA-OAEP",
            hash: "SHA-256"
        },
        true,
        ["decrypt"]
    ).then((pvtKey) => {
        console.log("pvtKey " + pvtKey);

        let encryptedData = "Bubb/cepSt/BZAoPjjsRtmy0Y/50cwcP6J0GvKavFEfLYzAkMi/JbueGILIWnllnENZM0w133JoVpPPT9B21hD2p8z2CzuOmYV4BnVV97i0oS0xawJPWdUwc6Vp+JaN9TrEX98FwCtQZHKtViyA8+Bnmu2LBKl5N/QipKIX+Dnc=";
        window.crypto.subtle.decrypt(
            { name: "RSA-OAEP "},
            pvtKey,
            str2ab(window.atob(encryptedData))
        ).then((decryptedData) => {
            console.log("decrypted " + decryptedData + " Rohit Das");
        }).catch((err) => {console.log("decrypt error " + err);});
    }).catch((err) => {console.log("key import error " + err);});
}

解决办法

你的代码里有三个关键问题:

  1. 解密配置的算法名称多了空格
    在decrypt方法的配置对象里,name: "RSA-OAEP "末尾多了一个空格,Web Crypto API会把这个当成不合法的算法名称,直接导致报错。需要改成name: "RSA-OAEP"。

  2. PEM密钥处理未清理换行符
    提取私钥的DER部分时,没有去除PEM中的换行符,会导致atob解码出错。需要先把PEM里的换行符去掉再处理。

  3. 解密结果未转换为可读字符串
    解密得到的是ArrayBuffer,直接打印只会显示[object ArrayBuffer],需要用TextDecoder转换成字符串才能看到明文。

修正后的代码如下:

function str2ab(str) {
    const bufView = new Uint8Array(str.length);
    for (let i = 0, strLen = str.length; i < strLen; i++) {
      bufView[i] = str.charCodeAt(i);
    }
    return bufView.buffer;
}

function decrypto() {
    let pvtPem = `-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALs/oFSzn8/01vLO
2qxe+FgcV1exBDmvDpHzQykfooiDBmiGlMsi5khSOgb/0b4Cu2/UUcvW6849+qVS
yPYe1VszwLMTna0H/P+QjIsDoaMpi346TZlSBYy4dcUEgIA1ot33dWbVE1lNyPiT
kH/dRP7NnyOLfRgmM+gsmu3HxUNNAgMBAAECgYEApO8ri9BYwbWZrHCWX2Sb/gig
ysZKwYC4JckP7GZIJVS8TU/WOoQ4MZX0NPwbRPJlJeDwV8utE5K2d+9OwrRwGw7n
ZirNolvYSnBeZ8pKRwseQA2fzfCMubZcttjpyb1DfyMXwQj6oCGxuFXPLUWT8/1/
9PkPAWa8uOZhinD/fwUCQQDcxcdCn9rAFLOn1g3EJzSD56GGu6RIrkx8kdBofTYr
nwYnriiX8a//hlRg4BiOv1FdoTHFJi3QAOobX8tiUBK7AkEA2SBxS2TDOj3HJByI
32D0HY1a/O3VBEF4mkRi60HtDYRxL7fmCxebgzFytGxIQr4h+VxFqlABDoDd5vd3
cSS1lwJARm3FumhalYpFIda0f43uP+Il8mBr8U/BUMAHlz3SiSnrAb+abZaJid+l
jV4QF4HLCC6DPRyH4uJXzLHLpSpcPwJARYVVwUYqHGPbd3yLdrqcbznrgEDGi+5K
p1puMdWSCVn2w8imJ7cPXBphF9Pz7yrhxe39gGLNc89fPazO2bNfUQJBALBdr/G3
KYecLkwJeR0okQ8biSr1mAC+VhqlF9JW4pDiw4jrud6R+M4+WjU5nGjxPCfhbEHA
PCahv9z7njXL2qA=
-----END PRIVATE KEY-----`;
    const pemHeader = "-----BEGIN PRIVATE KEY-----";
    const pemFooter = "-----END PRIVATE KEY-----";
    // 清理PEM中的换行符后再提取DER部分
    const cleanedPem = pvtPem.replace(/\n/g, '');
    let pvtDer = cleanedPem.substring(pemHeader.length, cleanedPem.length - pemFooter.length);
    let binaryDer = str2ab(window.atob(pvtDer));
    
    window.crypto.subtle.importKey(
        "pkcs8",
        binaryDer,
        {
            name: "RSA-OAEP",
            hash: "SHA-256"
        },
        true,
        ["decrypt"]
    ).then((pvtKey) => {
        let encryptedData = "Bubb/cepSt/BZAoPjjsRtmy0Y/50cwcP6J0GvKavFEfLYzAkMi/JbueGILIWnllnENZM0w133JoVpPPT9B21hD2p8z2CzuOmYV4BnVV97i0oS0xawJPWdUwc6Vp+JaN9TrEX98FwCtQZHKtViyA8+Bnmu2LBKl5N/QipKIX+Dnc=";
        // 清理密文中可能存在的换行符
        encryptedData = encryptedData.replace(/\n/g, '');
        
        window.crypto.subtle.decrypt(
            { name: "RSA-OAEP" }, // 去掉末尾多余的空格
            pvtKey,
            str2ab(window.atob(encryptedData))
        ).then((decryptedData) => {
            // 将ArrayBuffer转换为可读字符串
            const decoder = new TextDecoder();
            const plaintext = decoder.decode(decryptedData);
            console.log("解密结果: " + plaintext);
        }).catch((err) => {console.log("解密错误: " + err);});
    }).catch((err) => {console.log("密钥导入错误: " + err);});
}

另外注意:用echo "Rohit Das"加密时,命令会自动在明文末尾添加换行符,所以解密后的结果会是Rohit Das\n。如果不需要这个换行符,加密时可以用echo -n "Rohit Das"避免添加。

内容的提问来源于stack exchange,提问作者Rohit Das

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:04:57