2016-01-15 32 views
7

Fino ad ora ho usato JSEncrypt che è in grado di caricare una chiave pubblica da una stringa formattata PEM. E poi usalo con RSA per crittografare una stringa. Per esempio:Come caricare una chiave pubblica in formato PEM per la crittografia?

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

e poi:

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

mi piacerebbe fare lo stesso con WebCrypto, ma non capisco come fare. Ho provato le seguenti operazioni:

  1. rimuovere l'intestazione PEM
  2. Rimuovere il piè di pagina PEM
  3. Rimuovere CR/LF
  4. stringa Trim
  5. Decodifica la stringa Base64
  6. convertire il risultato a un ArrayBuffer

Quindi ho provato a importare la chiave:

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

Ho provato molti modi (decomprimere il formato ASN/DER, ecc.) Ma ottengo vari errori (dati DOMException, ecc.). Non so se il formato PEM è accettabile come formato supportato o se devo convertire la chiave in formato JSON Web Key, ecc.

C'è un modo semplice per farlo senza una libreria JS di terze parti?

+0

Non dovresti Base64-decodificare il Prima i dati PEM? – robertklep

+0

Se decodifico i dati e poi li converto in un ArrayBuffer ottengo una DOMException –

risposta

6

Ho trovato una risposta dopo alcuni test. Nel mio caso, ho usato JSEncrypt con PHP/openssl o phpseclib come fallback.

Con JSEncrypt, non è possibile scegliere l'algoritmo di crittografia. E questo ha un impatto sul riempimento utilizzato quando PHP decifra il valore crittografato. JSEncrypt usa:

  • RSASSA-PKCS1-v1_5
  • SHA-1 come metodo di hash

Se si vuole decifrare un messaggio, è necessario utilizzare l'opzione padding di default:

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

Ma WebCrypto non è compatibile con JSEncrypt (non siamo in grado di decifrare il messaggio con PHP con le stesse opzioni), in quanto:

  • WebCrypto può utilizzare SHA-1 come metodo hash, anche se non è raccomandato.
  • Ma WebCrypto vieta di utilizzare RSASSA-PKCS1-v1_5 a scopo di crittografia (è consentito solo per scopi di firma). Dovresti invece usare RSA-OAEP.

Se si tenta di decodificare il valore cifrato con le opzioni di default, si otterrà questo messaggio:

RSA_EAY_PRIVATE_DECRYPT:padding check failed 

Quindi, è necessario modificare l'opzione imbottitura come segue (in PHP):

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

per quanto riguarda la mia domanda iniziale, sì, si può importare una chiave in formato PEM, se si seguono i passi che ho menzionate nelle post

  1. rimuovere l'intestazione PEM
  2. Rimuovere thePEM footer
  3. Rimuovere CR/LF
  4. stringa Trim
  5. Decodifica la stringa Base64
  6. convertire il risultato di un codice completo 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); 

     }); 
     }); 
} 
+0

Grazie! Ecco come decifrare con openssl da cmdline: 'base64 -d ciphertext.base64 | openssl rsautl -decrypt -oaep -inkey private.pem -out cleartext' –

0

Per prima cosa scegli il tuo preferito Base64-to-ArrayBuffer e String-to-ArrayBuffer metodi, ad es.

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

(c'è una possibilità questi darà Uncaught (in promise) DOMException: Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).)

Poi si può scrivere una funzione encrypt nel Promessa stile

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

E, infine, di utilizzarlo

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