How to load a public key in PEM format for encryption?
直到现在,我一直使用JSEncrypt,它能够从PEM格式的字符串中加载公共密钥。然后将其与RSA结合使用以加密字符串。例如:
1 2 3 4 5 6 7 | <textarea id="pubkey">-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j 7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim GUrbOWEZdKZ9D5Sfo90EXocM5NtHou14aN8xkRWbN7x/RK5o9jfJwKmrC1fCm6tx 2Qwvx5kypWQUN6UpCQIDAQAB -----END PUBLIC KEY----- </textarea> |
然后:
1 2 | var encrypt = new JSEncrypt(); encrypt.setPublicKey($('#pubkey').val()); |
我想对WebCrypto进行同样的操作,但是我不知道该怎么做。我尝试了以下步骤:
然后我尝试导入密钥:
1 | cryptoSubtle.importKey("spki", publicKey, {name:"RSA-OAEP", hash: {name:"SHA-256"}}, false, ["encrypt"]); |
我尝试了许多方法(解压缩ASN / DER格式等),但遇到各种错误(DOMException数据等)。我不知道PEM格式是否可以接受为受支持的格式,或者我是否必须将其转换为JSON Web密钥格式等。
有没有第三方JS库的简单方法吗?
经过一些测试,我找到了答案。就我而言,我将JSEncrypt与PHP / openssl或phpseclib一起用作备用。
使用JSEncrypt,您无法选择加密算法。这会影响PHP解密加密值时使用的填充。 JSEncrypt使用:
- RSASSA-PKCS1-v1_5
- SHA-1作为哈希方法
如果要解密消息,则必须使用默认的填充选项:
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。
如果尝试使用默认选项对加密值进行解码,则会收到以下消息:
1 | RSA_EAY_PRIVATE_DECRYPT:padding check failed |
因此,您必须按照以下方式更改padding选项(在PHP中):
1 | openssl_private_decrypt(base64_decode($_POST['CipheredValue']), $ouput, $privateKey, OPENSSL_PKCS1_OAEP_PADDING); |
关于我的原始问题,是的,如果您遵循我在帖子中提到的步骤,可以导入PEM格式的密钥
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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('\ '); 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); }); }); } |
首先选择您喜欢的Base64-to-ArrayBuffer和String-to-ArrayBuffer方法,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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(); }); } |
(这些可能会给
然后您可以编写Promise样式的
1 2 3 4 5 6 | 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)); } |
最后,使用它
1 2 3 4 5 6 | encrypt('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+iOltdDtDdUq6u67L2Mb4HW5j\\ 7E1scmYtg2mnnQD85LxFICZv3I3rQ4wMulfcH+n9VCrifdu4vN89lRLKgsb9Kzim\\ GUrbOWEZdKZ9D5Sfo90EXocM5NtHou14aN8xkRWbN7x/RK5o9jfJwKmrC1fCm6tx\\ 2Qwvx5kypWQUN6UpCQIDAQAB', 'Hello World') .then(result => console.log(String.fromCharCode.apply(null, new Uint16Array(result)))); // ?鸵?????淓???????怀跰?偌诔?曶?????沶碅姮?????????茱?????蟌赅龙?偼幱?????勍 |