2016-01-15 15 views
7

Cho đến bây giờ tôi đã sử dụng JSEncrypt có thể tải khóa công khai từ một chuỗi được định dạng PEM. Và sau đó sử dụng nó với RSA để mã hóa một chuỗi. Ví dụ:Làm cách nào để tải khóa công khai ở định dạng PEM để mã hóa?

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

và sau đó:

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

Tôi muốn làm điều tương tự với WebCrypto nhưng tôi không hiểu làm thế nào để làm. Tôi đã thử các bước sau:

  1. Tháo tiêu đề PEM
  2. Tháo chân PEM
  3. Di CR/LF
  4. Trim chuỗi
  5. Giải mã chuỗi Base64
  6. Chuyển đổi kết quả đến một ArrayBuffer

Sau đó, tôi đã cố nhập khóa:

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

Tôi đã thử nhiều cách (giải nén định dạng ASN/DER, v.v.) Nhưng tôi gặp các lỗi khác nhau (dữ liệu DOMException, v.v.). Tôi không biết định dạng PEM có được chấp nhận dưới dạng định dạng được hỗ trợ hay không hoặc tôi phải chuyển đổi khóa ở định dạng Khóa Web JSON, v.v.

Có cách nào đơn giản để thực hiện mà không có thư viện JS của bên thứ ba không?

+0

Bạn không nên giải mã Base64 Dữ liệu PEM trước? – robertklep

+0

Nếu tôi giải mã dữ liệu và sau đó chuyển đổi nó thành một ArrayBuffer, tôi nhận được một DOMException –

Trả lời

6

Tôi đã tìm thấy câu trả lời sau một số bài kiểm tra. Trong trường hợp của tôi, tôi đã sử dụng JSEncrypt với PHP/openssl hoặc phpseclib làm dự phòng.

Với JSEncrypt, bạn không thể chọn thuật toán mã hóa. Và điều này có tác động đến việc đệm được sử dụng khi PHP giải mã giá trị được mã hóa. JSEncrypt sử dụng:

  • RSASSA-PKCS1-v1_5
  • SHA-1 là một phương pháp băm

Nếu bạn muốn giải mã một thông điệp, bạn phải sử dụng tùy chọn đệm mặc định:

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

Nhưng WebCrypto không tương thích với JSEncrypt (chúng tôi không thể giải mã thư bằng PHP với cùng tùy chọn), vì:

  • WebCrypto có thể sử dụng SHA-1 làm phương pháp băm, ngay cả khi không được khuyến nghị.
  • Nhưng WebCrypto cấm bạn sử dụng RSASSA-PKCS1-v1_5 cho mục đích mã hóa (chỉ được phép cho mục đích ký). Thay vào đó, bạn nên sử dụng RSA-OAEP.

Nếu bạn cố gắng để giải mã các giá trị được mã hóa với các tùy chọn mặc định, bạn sẽ nhận được tin nhắn này:

RSA_EAY_PRIVATE_DECRYPT:padding check failed 

Vì vậy, bạn phải thay đổi các tùy chọn đệm như sau (trong PHP):

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

về câu hỏi ban đầu của tôi, có bạn có thể nhập một chìa khóa ở định dạng PEM nếu bạn làm theo các bước tôi đã mentionned trong bài

  1. Tháo tiêu đề PEM
  2. Di thePEM chân
  3. Di CR/LF
  4. chuỗi Trim
  5. Giải mã chuỗi Base64
  6. Chuyển đổi kết quả để một mã ArrayBuffer

Complete:

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

Cảm ơn! Đây là cách giải mã với openssl từ cmdline: 'base64 -d ciphertext.base64 | openssl rsautl -decrypt -oaep -inkey private.pem -out cleartext' –

0

Trước tiên, hãy chọn các phương pháp yêu thích Base64-to-ArrayBufferChuỗi-to-ArrayBuffer của bạn, ví dụ:

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ó một cơ hội này sẽ cung cấp cho Uncaught (in promise) DOMException: Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).)

Sau đó, bạn có thể viết một chức năng encrypt trong Promise phong cách

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

Và cuối cùng, sử dụng nó

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