Я хочу зашифровать сообщения в чатах (веб-приложение) таким образом, чтобы никто, кроме получателей, не мог их расшифровать. Кажется, что шифрование RSA - хороший способ. Каждый пользователь (фактически, каждое устройство) будет иметь пару открытого и закрытого ключей, и каждое сообщение будет зашифровано открытым ключом получателя и расшифровано закрытым ключом получателя, поэтому каждое сообщение должно быть отправлено столько раз, сколько получатели того же самого. Я нашел руководство, который объясняет, как достичь этой цели. Шифрование / дешифрование рассчитывается в Service Worker, а закрытый ключ не отображается, а просто хранится в том же Worker.
Однако мне интересно, существует ли безопасный способ экспорта пары ключей с устройства, чтобы иметь возможность видеть ваши сообщения с другого устройства.
Я мог бы добавить messageType
под названием "exportKeys"
и получить оба ключа, как в приведенном ниже примере:
Из приведенного выше руководства добавление типа сообщения и функции "exportKeys":
self.window = self // This is required for the jsencrypt library to work within the web worker
// Import the jsencrypt library
self.importScripts('https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js');
let crypt = null
let privateKey = null
/** Webworker onmessage listener */
onmessage = function(e) {
const [ messageType, messageId, text, key ] = e.data
let result
switch (messageType) {
case 'generate-keys':
result = generateKeypair()
break
case 'encrypt':
result = encrypt(text, key)
break
case 'decrypt':
result = decrypt(text)
break
case 'exportKeys':
result = exportKeys(key)
break
}
// Return result to the UI thread
postMessage([ messageId, result ])
}
/** Generate and store keypair */
function generateKeypair () {
crypt = new JSEncrypt({default_key_size: 2056})
privateKey = crypt.getPrivateKey()
// Only return the public key, keep the private key hidden
return crypt.getPublicKey()
}
/** Encrypt the provided string with the destination public key */
function encrypt (content, publicKey) {
crypt.setKey(publicKey)
return crypt.encrypt(content)
}
/** Decrypt the provided string with the local private key */
function decrypt (content) {
crypt.setKey(privateKey)
return crypt.decrypt(content)
}
/** Export keys */
function exportKeys (publicKey) {
return {
publicKey: publicKey,
privateKey: privateKey
}
}
Затем я мог бы показать пользователю QR-код или что-то еще, чтобы экспортировать его ключи на другое устройство, и позволить ему сделать противоположное от нового с чем-то вроде "importKeys"
.
Хотя это может сработать, я бы раскрыл закрытый ключ с помощью "exportKeys"
также на стороне клиента. Но будет ли это проблемой для безопасности, если вы на самом деле не отправляете закрытый ключ через Интернет?
Есть еще какой совет по этому поводу?
Какую версию PHP вы используете? Если PHP 7.2, то я бы посоветовал вам использовать функции sodium
, а точнее sodium_crypto_box
php.net/manual/en/function.sodium-crypto-box.php
@PeterDarmis Здесь задействован только Javascript.
@Manolo, извините, тогда плохо ... В этом случае вы можете использовать NaCl.js github.com/tonyg/js-nacl для тех же функций или использовать любую реализацию libsodium в javascript в github, с которой вам удобно.
@PeterDarmis Вы имеете в виду, как и appleapple, зашифровать закрытый ключ перед его экспортом и требовать ключ для его импорта?
@Manolo нет, я имел в виду библиотеку libsodium, я думаю, что она имеет ту же функциональность, что и та, которую вы используете. Использование Service Worker - это здорово, но зачем вообще экспортировать ключи? Предложите пользователю зашифровать оба ключа, используя фразу по своему выбору. Когда на другом устройстве расшифруйте, используя эту фразу.
@Manolo В случае, если вы можете взаимодействовать с Service Worker с этой страницы, передайте фразу пользователя вашему Service Worker и получите ключи, зашифрованные с помощью парольной фразы. Также, пожалуйста, прочтите этот gist.github.com/atoponce/… и используйте что-то отличное от RSA
В случае сообщений private
и encrypted
, возможно, вы могли бы использовать реализацию libsodium в javascript, такую как js-nacl или libsodium для реализации Public-key authenticated encryption using crypto_box
. Большая часть функций аналогична той, которую вы уже используете. Чтобы лучше объяснить:
Отправитель и получатель должны иметь набор ключей (общественный и частный) для шифрования / дешифрования сообщений.
Отправителю нужны sender-PrivateKey
, recipient-PublicKey
и nonce
для шифрования сообщения.
Получателю нужны recipient-PrivateKey
, sender-PublicKey
и nonce
для расшифровки сообщения.
Пример кода взят со страницы js-nacl на Github.
senderKeypair = nacl.crypto_box_keypair();
recipientKeypair = nacl.crypto_box_keypair();
message = nacl.encode_utf8("Hello!");
nonce = nacl.crypto_box_random_nonce();
packet = nacl.crypto_box(message, nonce, recipientKeypair.boxPk, senderKeypair.boxSk);
decoded = nacl.crypto_box_open(packet, nonce, senderKeypair.boxPk, recipientKeypair.boxSk);
"Hello!" === nacl.decode_utf8(decoded); // always true
I want to encrypt messages in chat rooms (web app) in a way that it wouldn't be possible for anyone to decrypt them except for the receivers. It seems that RSA encryption is a good way.
Я не знаю, подходит ли шифрование RSA. Согласно тому, что считается лучшей тактикой Лучшие практики криптографии - асимметричное шифрование, для шифрования лучше использовать функции libsodium (NaCL), чем RSA.
Последний вопрос: как экспортировать ключи, не раскрывая их?
Вы можете экспортировать ключи, используя nacl.crypto_box_seed_keypair(Uint8Array)
и вводимые пользователем данные (для преобразования в Uint8Array используйте nacl.encode_utf8 (строка)).
nacl.crypto_box_seed_keypair(Uint8Array)
Produces an encrypted authenticated box keypair from its argument. A given binary input will always produce the same keypair as output.
The input may be of any length. The input is hashed once with sha512, and the first 32 bytes of the result are taken as the 32-byte secret key, which is then passed to nacl.crypto_box_keypair_from_raw_sk.
Может быть, использовать одноразовый токен / ключ доступа для (шифрования и) синхронизации реального ключа? так что, по крайней мере, настоящий ключ не будет отображаться напрямую (устройством записи экрана или чем-то еще)