如何加载PEM格式的公钥进行加密? [英] How to load a public key in PEM format for encryption?

查看:194
本文介绍了如何加载PEM格式的公钥进行加密?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Until now I used JSEncrypt which is able to load a public key from a PEM formatted string. And then use it with RSA in order to encrypt a string. For example :

<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做同样的事情,但我不明白该怎么做。我尝试了以下步骤:

I'd like to do the same with WebCrypto but I don't understand how to do. I've tried the following steps:


  1. 删除PEM标题

  2. 删除PEM页脚

  3. 删除CR / LF

  4. 修剪字符串

  5. 解码Base64字符串

  6. 将结果转换为ArrayBuffer

  1. Remove the PEM header
  2. Remove the PEM footer
  3. Remove CR/LF
  4. Trim string
  5. Decode the Base64 string
  6. Convert the result to an ArrayBuffer

然后我尝试导入密钥:

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

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

I tried many ways (unpack the ASN/DER format, etc.) But I get various errors (DOMException data, etc.). I don't know if the PEM format is acceptable as a supported format or if I must convert the key in JSON Web Key format, etc.

有没有简单的方法没有第三方JS库的话吗?

Is there a simple way to do it without a 3rd-party JS library ?

推荐答案

经过一些测试后我找到了答案。就我而言,我使用JSEncrypt和PHP / openssl或phpseclib作为后备。

I've found an answer after some tests. In my case, I used JSEncrypt with PHP/openssl or phpseclib as a fallback.

使用JSEncrypt,您无法选择加密算法。这会对PHP解密加密值时使用的 padding 产生影响。 JSEncrypt使用:

With JSEncrypt, you can't choose the encryption algorithm. And this has an impact to the padding used when PHP deciphers the encrypted value. JSEncrypt uses:


  • RSASSA-PKCS1-v1_5

  • SHA-1作为哈希方法

如果要解密消息,则必须使用默认的填充选项:

If you want to decipher a message, you have to use the default padding option:

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

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

But WebCrypto is not compatible with JSEncrypt (we cannot decrypt the message with PHP with the same options), because:


  • WebCrypto可以使用SHA-1作为哈希方法,即使不推荐使用。

  • 但是WebCrypto禁止您使用RSASSA-PKCS1-v1_5进行加密(仅允许进行签名)。您应该使用RSA-OAEP。

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

If you try to decode the encrypted value with the default options, you'll get this message:

RSA_EAY_PRIVATE_DECRYPT:padding check failed

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

So, you have to change the padding option as follow (in PHP):

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

关于我的原始问题,是的,如果您按照I'的步骤输入PEM格式的密钥在帖子中提到

Regarding my original question, yes you can import a key in PEM format if you follow the steps I've mentionned in the post


  1. 删除PEM标题

  2. 删除PDF页脚

  3. 删除CR / LF

  4. 修剪字符串

  5. 解码Base64字符串

  6. 转换结果为ArrayBuffer

  1. Remove the PEM header
  2. Remove thePEM footer
  3. Remove CR/LF
  4. Trim string
  5. Decode the Base64 string
  6. Convert the result to an 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);

        });
      });
}

这篇关于如何加载PEM格式的公钥进行加密?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆