How to download a public key in PEM format for encryption?

So far I have been using JSEncrypt, which was able to load the public key from a string in PEM format. And then use it with RSA to encrypt the string. For example:

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

and then:

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

I would like to do the same with WebCrypto, but I do not understand how to do this. I tried the following steps:

  • Delete PEM Header
  • Remove the PEM footer
  • Remove CR / LF
  • Trim line
  • Decode Base64 string
  • Convert result to ArrayBuffer

Then I tried to import the key:

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

I tried many ways (unpacked the ASN / DER format, etc.), but I get various errors (DOMException data, etc.). I do not know if the PEM format is acceptable as a supported format or if I have to convert the key to JSON Web Key format, etc.

Is there an easy way to do this without a third-party JS library?

+7
javascript rsa webcryptoapi
source share
2 answers

I found the answer after some tests. In my case, I used JSEncrypt with PHP / openssl or phpseclib as a backup.

With JSEncrypt, you cannot select an encryption algorithm. And this affects the padding used when PHP decrypts the encrypted value. JSEncrypt uses:

  • RSASSA-PKCS1-v1_5
  • SHA-1 as a hash method

If you want to decrypt the message, you must use the default add-on option:

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

But WebCrypto is incompatible with JSEncrypt (we cannot decrypt the message with PHP with the same parameters), because:

  • WebCrypto can use SHA-1 as a hash method, even if it is not recommended.
  • But WebCrypto forbids you to use RSASSA-PKCS1-v1_5 for encryption purposes (this is only allowed for signature purposes). Instead, you should use RSA-OAEP.

If you try to decode the encrypted value with the default parameters, you will receive this message:

 RSA_EAY_PRIVATE_DECRYPT:padding check failed 

So, you need to change the fill parameter as it should (in PHP):

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

As for my initial question, yes, you can import the key in PEM format if you follow the steps that I mentioned in the message

  • Delete PEM Header
  • Remove the footer
  • Remove CR / LF
  • Trim line
  • Decode Base64 string
  • Convert result to ArrayBuffer

Full code:

 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); }); }); } 
+8
source share

First select your favorite Base64-ArrayBuffer and String-to-ArrayBuffer methods, e.g.

 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(); }); } 

(There is a chance they will give Uncaught (in promise) DOMException: Only secure origins are allowed (see: https://goo.gl/Y0ZkNV). )

Then you can write the encrypt function in Promise style

 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)); } 

And finally, using it

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

All Articles