我正在尝试使用 crypto.subtle.decrypt 解密密文(使用 openssl 生成)并不断收到以下错误:
SyntaxError: An invalid or illegal string was specified
同样的密文在在线RSAtools和openssl中很容易被解密。我已经尝试了 stackoverflow 中出现的一些示例,例如 here、here 和 here,但即使是那些密文也失败了。我怀疑我在加密时可能错过了一些选项,但不确定是哪一个。
我使用以下方法生成密钥:
$ 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 函数中使用 crypto.subtle:
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);});
}
解密成功如果:
importKey()
调用中使用"SHA-1"
(而不是"SHA-256"
)。这也意味着发布的密文实际上并不是使用发布的 OpenSSL 语句(对两个摘要应用 SHA-256)生成的。decrypt()
调用中,空格从算法名称规范中删除("RSA-OAEP"
而不是 "RSA-OAEP "
)如果两者都已修复,则解密有效:
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-1"
},
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 " + new TextDecoder().decode(decryptedData));// + " Rohit Das");
}).catch((err) => {console.log("decrypt error " + err);});
}).catch((err) => {console.log("key import error " + err);});
}
decrypto();
这一点通过在线工具解密得到确认。我使用了https://8gwifi.org/rsafunctions.jsp和选项
RSA/NONE/OAEPWithSHA1AndMGF1Padding
或RSA/ECB/OAEPWithSHA-1AndMGF1Padding
(为此通过https将密钥格式从PKCS#8转换为PKCS#1:/ /8gwifi.org/pemconvert.jsp 是必要的)。使用发布的 OpenSSL 语句生成的密文(即两个摘要均使用 SHA-256)例如:
Tu8MHqnXCrRHZjKgMJx7CF4b2r6jz1ame7+qCFJ6ZkfgzgJc1qDX5xkyN28vZMzhEiO42iFGEeUoYDA4SH+RFPoGPbM94/IFvZREgpEow0a4scMvaYfKCK76gbb5P3UbEIwcgN+QBtsHCEC/aPJmtPzEl1jUNO94fjUkjrGaKwM=
如果在 WebCrypto 代码中应用 SHA-256,则按预期正确解密:
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 = "Tu8MHqnXCrRHZjKgMJx7CF4b2r6jz1ame7+qCFJ6ZkfgzgJc1qDX5xkyN28vZMzhEiO42iFGEeUoYDA4SH+RFPoGPbM94/IFvZREgpEow0a4scMvaYfKCK76gbb5P3UbEIwcgN+QBtsHCEC/aPJmtPzEl1jUNO94fjUkjrGaKwM=";
window.crypto.subtle.decrypt(
{ name: "RSA-OAEP"},
pvtKey,
str2ab(window.atob(encryptedData))
).then((decryptedData) => {
console.log("decrypted " + new TextDecoder().decode(decryptedData));// + " Rohit Das");
}).catch((err) => {console.log("decrypt error " + err);});
}).catch((err) => {console.log("key import error " + err);});
}
decrypto();