Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何加载PEM格式的公钥进行加密?_Javascript_Rsa_Webcrypto Api - Fatal编程技术网

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

Javascript 如何加载PEM格式的公钥进行加密?,javascript,rsa,webcrypto-api,Javascript,Rsa,Webcrypto Api,到目前为止,我使用JSEncrypt,它能够从PEM格式的字符串加载公钥。然后将其与RSA一起使用以加密字符串。例如: <textarea id="pubkey">-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j 7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim GUrbO

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

<textarea id="pubkey">-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j
7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim
GUrbOWEZdKZ9D5Sfo90EXocM5NtHou14aN8xkRWbN7x/RK5o9jfJwKmrC1fCm6tx
2Qwvx5kypWQUN6UpCQIDAQAB
-----END PUBLIC KEY-----
</textarea>
我也想用WebCrypto做同样的事情,但我不知道怎么做。我尝试了以下步骤:

  • 拆下PEM收割台
  • 移除PEM页脚
  • 拆下CR/LF
  • 修剪线
  • 解码Base64字符串
  • 将结果转换为ArrayBuffer
  • 然后我尝试导入密钥:

    cryptoSubtle.importKey("spki", publicKey, {name: "RSA-OAEP", hash: {name: "SHA-256"}}, false, ["encrypt"]);
    
    我尝试了很多方法(解包ASN/DER格式等),但我得到了各种错误(DomeException数据等)。我不知道PEM格式是否可以作为支持的格式接受,或者我是否必须将密钥转换为JSON Web密钥格式,等等


    在没有第三方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中):

    关于我最初的问题,是的,如果你按照我在文章中提到的步骤,你可以导入PEM格式的密钥

  • 拆下PEM收割台
  • 删除项目页脚
  • 拆下CR/LF
  • 修剪线
  • 解码Base64字符串
  • 将结果转换为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);
    
            });
          });
    }
    
    var crypto=window.crypto | | window.msCrypto;
    var加密算法={
    名称:“RSA-OAEP”,
    散列:{
    名称:“SHA-1”
    }
    };
    函数arrayBufferToBase64String(arrayBuffer){
    var byteArray=新的UINT8阵列(arrayBuffer)
    var byteString='';
    对于(var i=0;i 0&&
    第[i]行。indexOf('-BEGIN-RSA私钥-')<0&&
    行[i]。indexOf('-BEGIN RSA公钥-')<0&&
    行[i]。indexOf('-BEGIN PUBLIC KEY-')<0&&
    行[i]。indexOf('-END公钥-')<0&&
    行[i]。indexOf('-END RSA私钥-')<0&&
    第[i]行。indexOf('-END RSA公钥-')<0){
    编码+=行[i].trim();
    }
    }
    返回base64StringToArrayBuffer(编码);
    }
    功能导入公用事业(pemKey){
    返回新承诺(函数(解析){
    var importer=crypto.minute.importKey(“spki”,convertPemToBinary(pemKey),encryptAlgorithm,false,[“encrypt”]);
    导入器。然后(函数(键){
    决心(关键);
    });
    });
    }
    if(加密微妙){
    开始=新日期().getTime();
    importPublicKey($('#pubkey').val())。然后(函数(键){
    加密(加密算法、密钥、textToArrayBuffer($('#txtClear').val())。然后(函数(加密数据){
    cipheredValue=ArrayBufferToBase64字符串(加密数据);
    console.log(加密值);
    });
    });
    }
    
    首先选择您最喜欢的Base64 to ArrayBuffer和String to ArrayBuffer方法,例如

    function b64ToArrayBuffer(b64) {
        return new Promise((res, rej) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', 'data:application/octet-stream;base64,' + b64);
            xhr.responseType = 'arraybuffer';
            xhr.addEventListener('load', e => res(xhr.response));
            xhr.addEventListener('error', e => rej(xhr));
            xhr.send();
        });
    }
    
    function stringToArrayBuffer(str) {
        return new Promise((res, rej) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', 'data:text/plain,' + str);
            xhr.responseType = 'arraybuffer';
            xhr.addEventListener('load', e => res(xhr.response));
            xhr.addEventListener('error', e => rej(xhr));
            xhr.send();
        });
    }
    
    (有一种可能性,这些将给
    Uncaught(在承诺中)DOMEException:只允许安全来源(参见:https://goo.gl/Y0ZkNV)(

    然后,您可以使用Promise样式编写一个
    加密
    函数

    最后,使用它

    encrypt('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j\
    7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim\
    GUrbOWEZdKZ9D5Sfo90EXocM5NtHou14aN8xkRWbN7x/RK5o9jfJwKmrC1fCm6tx\
    2Qwvx5kypWQUN6UpCQIDAQAB', 'Hello World')
        .then(result => console.log(String.fromCharCode.apply(null, new Uint16Array(result))));
    // 䍞鸵즱ය㥬ᬍ㖆淓䛿⫵�ɪꤿᮌ怀跰届쇎偌诔락曶락ه͌쥻쨋沶碅姮갣ꤠ퉥�ﮕ컙郞ꦨꉣ茱닦ꥋ༈쿵⇲蟌赅龙Ⲯ偼幱䋚⫛Ɂౖ勍
    

    你不应该先对PEM数据进行Base64解码吗?如果我对数据进行解码,然后将其转换为ArrayBuffer,我会得到一个DomeException谢谢!这是如何从cmdline使用openssl进行解密:
    base64-d ciphertext.base64 | openssl rsautl-decrypt-oaep-inkey private.pem-out cleartext
    function b64ToArrayBuffer(b64) {
        return new Promise((res, rej) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', 'data:application/octet-stream;base64,' + b64);
            xhr.responseType = 'arraybuffer';
            xhr.addEventListener('load', e => res(xhr.response));
            xhr.addEventListener('error', e => rej(xhr));
            xhr.send();
        });
    }
    
    function stringToArrayBuffer(str) {
        return new Promise((res, rej) => {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', 'data:text/plain,' + str);
            xhr.responseType = 'arraybuffer';
            xhr.addEventListener('load', e => res(xhr.response));
            xhr.addEventListener('error', e => rej(xhr));
            xhr.send();
        });
    }
    
    function encrypt(b64_key, clear_text) {
        return b64ToArrayBuffer(b64_key)
            .then(buffer => window.crypto.subtle.importKey("spki", buffer, {name: "RSA-OAEP", hash: {name: "SHA-256"}}, false, ["encrypt"]))
            .then(key => new Promise((res, rej) => stringToArrayBuffer(clear_text).then(buffer => res({key, buffer}))))
            .then(data => window.crypto.subtle.encrypt({name: "RSA-OAEP", hash: {name: "SHA-256"}}, data.key, data.buffer));
    }
    
    encrypt('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j\
    7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim\
    GUrbOWEZdKZ9D5Sfo90EXocM5NtHou14aN8xkRWbN7x/RK5o9jfJwKmrC1fCm6tx\
    2Qwvx5kypWQUN6UpCQIDAQAB', 'Hello World')
        .then(result => console.log(String.fromCharCode.apply(null, new Uint16Array(result))));
    // 䍞鸵즱ය㥬ᬍ㖆淓䛿⫵�ɪꤿᮌ怀跰届쇎偌诔락曶락ه͌쥻쨋沶碅姮갣ꤠ퉥�ﮕ컙郞ꦨꉣ茱닦ꥋ༈쿵⇲蟌赅龙Ⲯ偼幱䋚⫛Ɂౖ勍