【问题标题】:Export/Import RSA key pair from web app从 Web 应用程序导出/导入 RSA 密钥对
【发布时间】:2018-08-09 12:44:15
【问题描述】:

我想加密聊天室(网络应用程序)中的消息,使除接收者之外的任何人都无法解密它们。看来RSA加密是个好办法。每个用户(实际上是每个设备)都有一个公钥-私钥对,每条消息都将使用接收方公钥加密并使用接收方私钥解密,因此每条消息的发送次数应与接收方相同。 我找到了tutorial,它解释了如何实现该目标。加密/解密在 Service Worker 中计算,私钥不暴露,只是存储在同一个 Worker 中。

但是,我想知道是否有一种安全的方法可以从设备导出密钥对,以便能够从另一台设备查看您的消息。 我可以添加一个名为 "exportKeys"messageType 并检索两个键,如下例所示:

从上面的教程中添加“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 来实现相同的功能,或者在 github 中使用任何您熟悉的 javascript 中的 libsodium 实现。
  • @PeterDarmis 你的意思是像苹果一样,在导出之前加密私钥并需要密钥来导入它吗?

标签: javascript encryption rsa service-worker


【解决方案1】:

如果是privateencrypted 消息,也许您可​​以在javascript 中使用libsodium 实现,例如js-nacl,或libsodium 来实现Public-key authenticated encryption using crypto_box。大多数功能与您已经使用的功能相似。为了更好地解释:

发送者和接收者必须拥有一组密钥(publicprivate)才能加密/解密消息。

发件人需要sender-PrivateKeyrecipient-PublicKeynonce 来加密邮件。

收件人需要recipient-PrivateKeysender-PublicKeynonce 来解密邮件。

示例代码取自 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

我想以某种方式加密聊天室(网络应用)中的消息 除了 接收器。看来RSA加密是个好办法。

我不知道 RSA 加密是不是一个好方法。根据被认为是最佳策略Cryptographic Best Practices - Asymmetric Encryption 的说法,使用 libsodium(NaCL) 函数进行加密比使用 RSA 更好。

最后问的是如何在不泄露密钥的情况下导出密钥?

您可以使用 nacl.crypto_box_seed_keypair(Uint8Array) 和用户提供的输入导出密钥(要转换为 Uint8Array 使用 nacl.encode_utf8(String))。

nacl.crypto_box_seed_keypair(Uint8Array)

  • 根据其参数生成一个加密的经过身份验证的盒子密钥对。一种 给定的二进制输入将始终生成与输出相同的密钥对。

  • 输入可以是任意长度。输入用 sha512 散列一次, 并将结果的前 32 个字节作为 32 字节秘密 密钥,然后传递给 nacl.crypto_box_keypair_from_raw_sk。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-20
    • 2021-09-17
    • 2017-10-18
    • 1970-01-01
    • 1970-01-01
    • 2010-10-21
    相关资源
    最近更新 更多