javascript - 如何加载 PEM 格式的公钥进行加密?

标签 javascript rsa webcrypto-api

到目前为止,我使用的是 JSEncrypt,它能够从 PEM 格式的字符串中加载公钥。然后将其与 RSA 一起使用以加密字符串。例如:

<textarea id="pubkey">-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j
7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim
GUrbOWEZdKZ9D5Sfo90EXocM5NtHou14aN8xkRWbN7x/RK5o9jfJwKmrC1fCm6tx
2Qwvx5kypWQUN6UpCQIDAQAB
-----END PUBLIC KEY-----
</textarea>

然后:

var encrypt = new JSEncrypt();
encrypt.setPublicKey($('#pubkey').val());

我想对 WebCrypto 做同样的事情,但我不知道该怎么做。我尝试了以下步骤:

  1. 删除 PEM header
  2. 删除 PEM 页脚
  3. 删除 CR/LF
  4. trim 字符串
  5. 解码Base64字符串
  6. 将结果转换为 ArrayBuffer

然后我尝试导入 key :

cryptoSubtle.importKey("spki", publicKey, {name: "RSA-OAEP", hash: {name: "SHA-256"}}, false, ["encrypt"]);

我尝试了很多方法(解压 ASN/DER 格式等),但出现各种错误(DOMException 数据等)。我不知道 PEM 格式是否可以作为受支持的格式,或者我是否必须将 key 转换为 JSON Web key 格式等。

有没有无需第 3 方 JS 库的简单方法?

最佳答案

经过一些测试,我找到了答案。就我而言,我将 JSEncrypt 与 PHP/openssl 或 phpseclib 一起用作备用。

使用 JSEncrypt,您无法选择加密算法。这对 PHP 解密加密值时使用的 填充 有影响。 JSEncrypt 使用:

  • RSASSA-PKCS1-v1_5
  • SHA-1 作为哈希方法

如果你想破译一条消息,你必须使用默认的填充选项:

openssl_private_decrypt(base64_decode($_POST['CipheredValue']), $ouput, $privateKey, OPENSSL_PKCS1_PADDING);

但 WebCrypto 与 JSEncrypt 不兼容(我们无法使用具有相同选项的 PHP 解密消息),因为:

  • WebCrypto 可以使用 SHA-1 作为哈希方法,即使不推荐。
  • 但 WebCrypto 禁止您使用 RSASSA-PKCS1-v1_5 进行加密(仅允许用于签名目的)。您应该改用 RSA-OAEP。

如果您尝试使用默认选项解码加密值,您将收到此消息:

RSA_EAY_PRIVATE_DECRYPT:padding check failed

因此,您必须按如下方式更改填充选项(在 PHP 中):

openssl_private_decrypt(base64_decode($_POST['CipheredValue']), $ouput, $privateKey, OPENSSL_PKCS1_OAEP_PADDING);

关于我原来的问题,是的,如果您按照我在帖子中提到的步骤操作,您可以导入 PEM 格式的 key

  1. 删除 PEM header
  2. 删除 PEM 页脚
  3. 删除 CR/LF
  4. trim 字符串
  5. 解码Base64字符串
  6. 将结果转换为 ArrayBuffer

完整代码:

var crypto = window.crypto || window.msCrypto;
var encryptAlgorithm = {
  name: "RSA-OAEP",
  hash: {
    name: "SHA-1"
  }
};

function arrayBufferToBase64String(arrayBuffer) {
  var byteArray = new Uint8Array(arrayBuffer)
  var byteString = '';
  for (var i=0; i<byteArray.byteLength; i++) {
    byteString += String.fromCharCode(byteArray[i]);
  }
  return btoa(byteString);
}

function base64StringToArrayBuffer(b64str) {
  var byteStr = atob(b64str);
  var bytes = new Uint8Array(byteStr.length);
  for (var i = 0; i < byteStr.length; i++) {
    bytes[i] = byteStr.charCodeAt(i);
  }
  return bytes.buffer;
}

function textToArrayBuffer(str) {
  var buf = unescape(encodeURIComponent(str)); // 2 bytes for each char
  var bufView = new Uint8Array(buf.length);
  for (var i=0; i < buf.length; i++) {
    bufView[i] = buf.charCodeAt(i);
  }
  return bufView;
}

function convertPemToBinary(pem) {
  var lines = pem.split('\n');
  var encoded = '';
  for(var i = 0;i < lines.length;i++){
    if (lines[i].trim().length > 0 &&
        lines[i].indexOf('-BEGIN RSA PRIVATE KEY-') < 0 && 
        lines[i].indexOf('-BEGIN RSA PUBLIC KEY-') < 0 &&
        lines[i].indexOf('-BEGIN PUBLIC KEY-') < 0 &&
        lines[i].indexOf('-END PUBLIC KEY-') < 0 &&
        lines[i].indexOf('-END RSA PRIVATE KEY-') < 0 &&
        lines[i].indexOf('-END RSA PUBLIC KEY-') < 0) {
      encoded += lines[i].trim();
    }
  }
  return base64StringToArrayBuffer(encoded);
}

function importPublicKey(pemKey) {
  return new Promise(function(resolve) {
    var importer = crypto.subtle.importKey("spki", convertPemToBinary(pemKey), encryptAlgorithm, false, ["encrypt"]);
    importer.then(function(key) { 
      resolve(key);
    });
  });
}


if (crypto.subtle) {

      start = new Date().getTime();
      importPublicKey($('#pubkey').val()).then(function(key) {
        crypto.subtle.encrypt(encryptAlgorithm, key, textToArrayBuffer($('#txtClear').val())).then(function(cipheredData) {
            cipheredValue = arrayBufferToBase64String(cipheredData);
            console.log(cipheredValue);

        });
      });
}

关于javascript - 如何加载 PEM 格式的公钥进行加密?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34814480/

相关文章:

javascript - 将数字转换为 ArrayBuffer

javascript - Playwright JS - 如果找不到元素/选择器,如何全局定义/更改超时?

javascript - 您如何强制 Google Charts vAxes 渲染?

javascript - DataTables根据某个值将数据替换为多列

java - 用Java重新实现RSA解密

encryption - Microsoft Edge中的公钥加密

javascript - 使用 clearTimeout 停止顺序功能/动画?

java - 在java中寻找ElGamal算法子群的生成器?

security - 从用户的 RSA 公钥计算用户的私钥

javascript - 如何使用 PBKDF2 的 Web Crypto API SubtleCrypto.deriveKey() 设置输出长度