我生成的私钥如下:

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

openssl rsa -pubout -in private_key.pem -out public_key.pem

openssl pkcs8 -topk8 -inform PEM -outform PEM -in private_key.pem -out private_key_pkcs8.pem -nocrypt

私钥被嵌入到代码片段中.然后,我加密了一个明文,如下所示:

echo -n "abcdef" | openssl rsautl -encrypt -pubin -inkey public_key.pem | openssl base64 > enc_simple.txt

这也嵌入在代码片段中.如果我执行以下操作:

base64 -d enc_simple.txt > enc_simple.bin
openssl rsautl -decrypt -inkey private_key_pkcs8.pem -in enc_simple.bin -out decrypted_simple.txt

我得到了"abcdef".因此,对于openssl,一切都很正常.然后我想使用Web Crypto API重新创建整个场景,但解密失败.导入私钥是可以的,但是其余的操作失败,OperationError没有错误消息.它极其晦涩难懂,很难调试.

我还跑过:

od -An -vtu1 enc_simple.bin

来查看这Uint8Array个值,并将它们与我从代码片段中获得的str2ab值进行比较.他们匹配!

因此,我遇到了没有错误消息的问题.我想知道为什么解密失败了.

const encryptedData = `gLiN3BLWTxbwpE5bwcFiB7Y/nK0H4iz9lK0sehMIJtoAPibDPZ8EYp8EnzsGFlKC
+GXse6Ka5YC7UKYn7xUvfBbCDSY7RJ/J9oAzpCEHvCnFOmrsvUtNEhP4w3LTaKL/
qKmjQZaPlZNWCmrzrW3g2A6DUCqLx5EQwCcwME2WjYVhuMZfZOKqtp+uukJMUf3h
cC9J8QkPCk1NZogCad29b7q7JLN1uWEQzOUgB6BqJfAp8kL92dftOy2gpWoGYraI
YPU3Tmp12txMyUY0yKsr4c2dOzxwWUW+ZssVkc6ZGqFVufADziZDp2sfFaz8yBuI
qek7S0KXd6A7qWBYBrzzyw==`; //abcdef
const privKeyPEM=`-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJiQlvlYbTqi/W
bLa7nzaF4Wz+ypQa01HD+UMHX25Uv3n6P69ucKs/OOdWbcenknAmSBK/VzgIsjlm
vak3y1LoYImMhm8R26Arv8Fv7jwQkbBxVnOn6yfTL2h1SRK+zFzWumfbl2vp7POG
s7CRQaK0HFPKroVlYHlinTlYXSlFqdaknH2ip1J2YPJ3O4w0/ME8gBZ0Mj1XkokB
Qc0Lk76BpvfTuBbzvLYI4bq0/jSoM6ANecqK+T/7/aSFxh2x8acJ/xzJg2H7I+de
pXZ1M9Mz0x94jWRfc9Q9qLgY6AuX7MUyb2YEs+vMxcim60fvktHAaXvn9vM0eauC
B+hpThohAgMBAAECggEAINub0zqAwe/EXuRYopxhqlBHkf77SKhdc2MnX4NanKyf
OYK6mnn6IZOoe/noDFUevc8QZ2vT8e8E1tBjT3px6PscUfH1F+dD5P4djp874cOv
Dbt3ndAELTVUhZLFYKA3HrdDiZTVfk0oozSWvAgEe/MGYkwz6YRiJgbWO6bsvLOa
5Ehhd7Ocxbs4B389ad3bVzRkk02/IRDEeO1z0+22Wv1NIe0Spzl6FVLspL6JdmEu
60rpShfwN7D0G4TQYYp/9Sh+zt0ThIF4kqQoURx5E2uQaDUAT206itMG2mUWYSR1
M37U+46kpWBz0jDaRFmqlySKrmkbx7kWXju8wP1ZAQKBgQDvlq4B3jAPd+Pu/Rxw
GORV5S8PdJrkQnx8BeAviGAVc2w6EMyRWusVzL95mpRS0t0E5xZtB2kLkVU/I08b
j+WqrGFqFp8niXYnJsien8nfLct0AhJVvVaPtt8lHP9SKniBOZc0owzFljnt+4/6
M4ifAiKvJYOqPSdGpJgvJq4TsQKBgQDXVxJCjUNInb4p0WWljxdcMpgSDPn53+BG
M/MrxVbTOvHhKsbNlglYxT/87J+8lFKS5Y9EkpqekCY1aGJ0PYfLBMowvNAwiP2s
89++PTuEO/hK16Xb8znZ7rUGdJdRUitBN8iVkWNLA2pzalcZkkHbIWQbDuJOPbdO
bo1u06U5cQKBgAgeliUQD5bmnD3kLAuMfGiAzNh8PieQLUHSvSc/OupfMALDwPsI
FsF1X+PSHka0SLM61aK6RpASy83I94xakxD2qJJ808X2PZ/UC6Z8ic3bcnKrA04O
jZlvPB643do+ADl45yvsfqlPjwUGqnlzN6UT4HMJFW42hlc5isLGT83xAoGBANT/
UxhxEfRp0wcaECjKeJjBkpmILFp0jynhiM3qzA7zZv0JissfdO9RbBGJHBczvtl+
J0/0kuv0OVbqgTfpBMBTZIsAuAzJ8+F2+AD8IDqT9uxQkcYVt0tRSc2w1VuioxZH
TyhiPoycPFcdADpS6MEPLi11c3NgqEf0IgFVZ0CBAoGBAMybGC+IImSDpZzLaSpz
KAD6Vyto7eK4pENH8bPcM2Hpy7aMGJkr6Q/NT34p/vSfFG2gm8ueILhvqQ8yB6h6
gtfHOEu7CC/8WL4Y/WehrpD+fTTSOd/C+JZJbXjHjJ55NX1JzgeCPWJP2QOyXE85
VLq+2V8bhsQxi4JylnH6suLB
-----END PRIVATE KEY-----`
const
    atob = (window || self)?.atob,
    subtle = (window || self).crypto.subtle,
    trimWs = (s) => s.replace(/\s*/g,""),
    trimHeader = (s) => s.replace(/-----(?:BEGIN|END) PRIVATE KEY-----/gi, "");
async function importPrivateKey (pemkey) {
    const binaryKey = atob(trimWs(trimHeader(pemkey)));
    const keyBuffer = new Uint8Array(binaryKey.length);
    for (let i = 0; i < binaryKey.length; i++) {
        keyBuffer[i] = binaryKey.charCodeAt(i);
    }
    return await subtle.importKey(
        "pkcs8",
        keyBuffer,
        { name: "RSA-OAEP", hash: "SHA-256"},
        false,
        ["decrypt"]
    );
}
async function decryptData (privateKey, encryptedData) {
      const decrypted = await subtle.decrypt(
          { name: "RSA-OAEP", hash: "SHA-256"},
          privateKey,
         str2ab(atob(trimWs(encryptedData)))
      );
      return (new TextDecoder('utf-8')).decode(decrypted);
}
function str2ab (binaryStr) {
    const len = binaryStr.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        bytes[i] = binaryStr.charCodeAt(i); // Accurately map each char to a byte
    }
    return bytes; // Convert to ArrayBuffer
};


(async () => {
  try {
    const privKey = await importPrivateKey(privKeyPEM);
    console.log(privKey);
    const decrypted = await decryptData(privKey, encryptedData);
  } catch (err) {
    console.error("Err:", err, "Err.massage:", err.message);
  }
})()

推荐答案

默认情况下,OpenSSLrsautl加密使用PKCS#1 v1.5填充,而WebCrypto代码使用SHA256(为both摘要、OAEP和MGF1摘要指定SHA256)应用OAEP.因此,解密失败.

由于WebCrypto不支持PKCS#1 v1.5填充,因此必须在OpenSSL端使用具有SHA256的OAEP才能成功解密.

OpenSSLrsautl支持OAEP,但仅支持SHA1.要使用SHA256,必须应用OpenSSLpkeyutl.使用选项-pkeyopt rsa_padding_mode:oaep指定OAEP,使用选项-pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256将OAEP和MGF1摘要设置为SHA256:

echo -n "abcdef" | openssl pkeyutl -encrypt -pubin -inkey public_key.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 | openssl base64 > enc_simple.txt

以这种方式生成的密文可以使用发布的WebCrypto代码进行解密(对于本测试,我使用了您发布的WebCrypto代码中的私钥,并从中提取了用于加密的公钥):

const encryptedData = `ETL+N6TbCcU+qoSrtVHqamrcLBM2upvS04+rvjeSL3ZA370jDMDLMjzszEakVJ7E
cA8mmY0GAK/ACFYV5Xc3KPXZVWFDaeTAu4tWY/TEftou/3zBWP9LjF4tViqWmxwC
3zgXM6lFaRBJMU/hIdc2mjNNS8/Rvc4HIS+6WBPXK50AOPySuak6m1EU3Zp2xeny
kGzH6+qotvJ3BC+OTXMAhaFwm+5hURY6rfAlYy5VX5NT2aD0FL0ggRF69Y+SMFZa
DDmaTiV1OwHuErm1T1k26QTgGcX/W+MWIlFBDXbJZxbIKIaHqquK2mN7qrGEQKVz
Ddh8MrfBs1aihAqgkXdLxg==`; //abcdef
const privKeyPEM=`-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJiQlvlYbTqi/W
bLa7nzaF4Wz+ypQa01HD+UMHX25Uv3n6P69ucKs/OOdWbcenknAmSBK/VzgIsjlm
vak3y1LoYImMhm8R26Arv8Fv7jwQkbBxVnOn6yfTL2h1SRK+zFzWumfbl2vp7POG
s7CRQaK0HFPKroVlYHlinTlYXSlFqdaknH2ip1J2YPJ3O4w0/ME8gBZ0Mj1XkokB
Qc0Lk76BpvfTuBbzvLYI4bq0/jSoM6ANecqK+T/7/aSFxh2x8acJ/xzJg2H7I+de
pXZ1M9Mz0x94jWRfc9Q9qLgY6AuX7MUyb2YEs+vMxcim60fvktHAaXvn9vM0eauC
B+hpThohAgMBAAECggEAINub0zqAwe/EXuRYopxhqlBHkf77SKhdc2MnX4NanKyf
OYK6mnn6IZOoe/noDFUevc8QZ2vT8e8E1tBjT3px6PscUfH1F+dD5P4djp874cOv
Dbt3ndAELTVUhZLFYKA3HrdDiZTVfk0oozSWvAgEe/MGYkwz6YRiJgbWO6bsvLOa
5Ehhd7Ocxbs4B389ad3bVzRkk02/IRDEeO1z0+22Wv1NIe0Spzl6FVLspL6JdmEu
60rpShfwN7D0G4TQYYp/9Sh+zt0ThIF4kqQoURx5E2uQaDUAT206itMG2mUWYSR1
M37U+46kpWBz0jDaRFmqlySKrmkbx7kWXju8wP1ZAQKBgQDvlq4B3jAPd+Pu/Rxw
GORV5S8PdJrkQnx8BeAviGAVc2w6EMyRWusVzL95mpRS0t0E5xZtB2kLkVU/I08b
j+WqrGFqFp8niXYnJsien8nfLct0AhJVvVaPtt8lHP9SKniBOZc0owzFljnt+4/6
M4ifAiKvJYOqPSdGpJgvJq4TsQKBgQDXVxJCjUNInb4p0WWljxdcMpgSDPn53+BG
M/MrxVbTOvHhKsbNlglYxT/87J+8lFKS5Y9EkpqekCY1aGJ0PYfLBMowvNAwiP2s
89++PTuEO/hK16Xb8znZ7rUGdJdRUitBN8iVkWNLA2pzalcZkkHbIWQbDuJOPbdO
bo1u06U5cQKBgAgeliUQD5bmnD3kLAuMfGiAzNh8PieQLUHSvSc/OupfMALDwPsI
FsF1X+PSHka0SLM61aK6RpASy83I94xakxD2qJJ808X2PZ/UC6Z8ic3bcnKrA04O
jZlvPB643do+ADl45yvsfqlPjwUGqnlzN6UT4HMJFW42hlc5isLGT83xAoGBANT/
UxhxEfRp0wcaECjKeJjBkpmILFp0jynhiM3qzA7zZv0JissfdO9RbBGJHBczvtl+
J0/0kuv0OVbqgTfpBMBTZIsAuAzJ8+F2+AD8IDqT9uxQkcYVt0tRSc2w1VuioxZH
TyhiPoycPFcdADpS6MEPLi11c3NgqEf0IgFVZ0CBAoGBAMybGC+IImSDpZzLaSpz
KAD6Vyto7eK4pENH8bPcM2Hpy7aMGJkr6Q/NT34p/vSfFG2gm8ueILhvqQ8yB6h6
gtfHOEu7CC/8WL4Y/WehrpD+fTTSOd/C+JZJbXjHjJ55NX1JzgeCPWJP2QOyXE85
VLq+2V8bhsQxi4JylnH6suLB
-----END PRIVATE KEY-----`
const
    atob = (window || self)?.atob,
    subtle = (window || self).crypto.subtle,
    trimWs = (s) => s.replace(/\s*/g,""),
    trimHeader = (s) => s.replace(/-----(?:BEGIN|END) PRIVATE KEY-----/gi, "");
async function importPrivateKey (pemkey) {
    const binaryKey = atob(trimWs(trimHeader(pemkey)));
    const keyBuffer = new Uint8Array(binaryKey.length);
    for (let i = 0; i < binaryKey.length; i++) {
        keyBuffer[i] = binaryKey.charCodeAt(i);
    }
    return await subtle.importKey(
        "pkcs8",
        keyBuffer,
        { name: "RSA-OAEP", hash: "SHA-256"},
        false,
        ["decrypt"]
    );
}
async function decryptData (privateKey, encryptedData) {
      const decrypted = await subtle.decrypt(
          { name: "RSA-OAEP", hash: "SHA-256"},
          privateKey,
         str2ab(atob(trimWs(encryptedData)))
      );
      return (new TextDecoder('utf-8')).decode(decrypted);
}
function str2ab (binaryStr) {
    const len = binaryStr.length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
        bytes[i] = binaryStr.charCodeAt(i); // Accurately map each char to a byte
    }
    return bytes; // Convert to ArrayBuffer
};


(async () => {
  try {
    const privKey = await importPrivateKey(privKeyPEM);
    //console.log(privKey);
    const decrypted = await decryptData(privKey, encryptedData);
    console.log(decrypted)
  } catch (err) {
    console.error("Err:", err, "Err.massage:", err.message);
  }
})()

在OpenSSL端,可以使用以下命令成功解密:

base64 -d enc_simple.txt > enc_simple.bin
openssl pkeyutl -decrypt -inkey private_key_pkcs8.pem -in enc_simple.bin -out decrypted_simple.txt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256

Javascript相关问答推荐

通过在页面上滚动来移动滚动条

在页面上滚动 timeshift 动垂直滚动条

调用removeEvents不起作用

Phaser 3 console. log()特定游戏角色的瓷砖属性

Chrome是否忽略WebAuthentication userVerification设置?""

D3 Scale在v6中工作,但在v7中不工作

jQuery s data()[storage object]在vanilla JavaScript中?'

将旋转的矩形zoom 到覆盖它所需的最小尺寸&S容器

实现JS代码更改CSS元素

rxjs插入延迟数据

如何在使用rhandsontable生成表时扩展数字输入验证?

用于在路径之间移动图像的查询

连接到游戏的玩家不会在浏览器在线游戏中呈现

未加载css colored颜色 ,无法将div设置为可见和不可见

使用Perl Selify::Remote::Driver执行Java脚本时出错

输入数据覆盖JSON文件

需要从对象生成列表

使用Java脚本筛选数组中最接近值最小的所有项

ReactJS Sweep Line:优化SciChartJS性能,重用wasmContext进行多图表渲染

:host::ng-Deep不将样式应用于material 复选框