使用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);}); }
解决办法
你的代码里有三个关键问题:
解密配置的算法名称多了空格
在decrypt方法的配置对象里,name: "RSA-OAEP "末尾多了一个空格,Web Crypto API会把这个当成不合法的算法名称,直接导致报错。需要改成name: "RSA-OAEP"。PEM密钥处理未清理换行符
提取私钥的DER部分时,没有去除PEM中的换行符,会导致atob解码出错。需要先把PEM里的换行符去掉再处理。解密结果未转换为可读字符串
解密得到的是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
相关产品推荐
相关产品推荐

