6

我想做一个测试应用程序,它使用libsodium从客户端到服务器进行通信。

许多语言有许多端口: C#PHP、...

并且总是有“鲍勃”和“爱丽丝”的例子。这很好,但他们从未展示如何以安全的方式通过网络交换公钥。

那么推荐的方法是如何交换“alice/client”和“bob/server”的公钥。

他们总是使用相同的文件或相同的机器来生成密钥对。

这是libsodium-php扩展的摘录:

$alice_kp = crypto_box_keypair();
$alice_secretkey = crypto_box_secretkey($alice_kp);
$alice_publickey = crypto_box_publickey($alice_kp);

$bob_kp = crypto_box_keypair();
$bob_secretkey = crypto_box_secretkey($bob_kp);
$bob_publickey = crypto_box_publickey($bob_kp);

$alice_to_bob_kp = crypto_box_keypair_from_secretkey_and_publickey
  ($alice_secretkey, $bob_publickey);

$bob_to_alice_kp = crypto_box_keypair_from_secretkey_and_publickey
  ($bob_secretkey, $alice_publickey);

$alice_to_bob_message_nonce = randombytes_buf(CRYPTO_BOX_NONCEBYTES);

$alice_to_bob_ciphertext = crypto_box('Hi, this is Alice',
                                      $alice_to_bob_message_nonce,
                                      $alice_to_bob_kp);

$alice_message_decrypted_by_bob = crypto_box_open($alice_to_bob_ciphertext,
                                                  $alice_to_bob_message_nonce,
                                                  $bob_to_alice_kp);

$bob_to_alice_message_nonce = randombytes_buf(CRYPTO_BOX_NONCEBYTES);

$bob_to_alice_ciphertext = crypto_box('Hi Alice! This is Bob',
                                      $bob_to_alice_message_nonce,
                                      $bob_to_alice_kp);

$bob_message_decrypted_by_alice = crypto_box_open($bob_to_alice_ciphertext,
                                                  $bob_to_alice_message_nonce,
                                                  $alice_to_bob_kp);
4

1 回答 1

9

Libsodium 不提供任何直接的密钥交换方法。您可以通过几种不同的方式安全地进行密钥交换:

  1. 通过防篡改的“带外通信”传递公钥,或者如果 com 通道对听众来说是安全的,则传递一个共享的秘密。(例如,BittorrentSync,一个包含从设备扫描到设备的共享密钥的二维码)

  2. 让受信任的第三方在未加密的情况下发送公钥之前对其进行签名。(例如 SSL/TLS 中的证书颁发机构)

  3. 证书固定(例如用于 Google URL 的 Google Chrome)

如果您可以控制客户

由于您可能不想重新实现 SSL/TLS,因此第一个或第三个选项可能是最好的。然而,第一个在客户端/服务器格式中有点困难,因为 PHP 服务器可能只有互联网可以通信。

但是,如果您控制客户端,则可以进行证书固定。也就是说,将 Bob 的公钥嵌入 Alice 的可执行文件中。然后 Alice 将只使用 Bob 的公钥加密消息。

例如,Alice 使用 Bob 的公钥和她自己的私钥来加密一些数据并将其(连同唯一的 nonce 和 Alice 的公钥)发送给 Bob。

Bob 使用他收到的公钥和他自己的私钥来解密和验证加密的数据包。Bob 可以安全地使用其中的任何数据,因为只有他和 Alice 可以解密它。

中间的男人呢?

好吧,Eve 可以阻止 Alice 的消息并发送 Eve 的公钥,但她会盲目工作,因为 Alice 只会发送用 Alice 的私钥和 Bob 的公钥加密的消息。

在鲍勃的眼里,她只是另一个客户。在爱丽丝的眼里,她会很沮丧,因为鲍勃似乎没有回应。

如果 Eve 向 Alice 发送消息,它们将始终被标记为无效,因为 Alice 使用 Alice 的私钥和 Bob 的公钥来尝试解密它们,但由于 Eve 没有 Bob 的私钥,这将失败。

如果 Alice 没有经过验证的可执行文件,Eve 可能会在她下载它时对其进行更改(并插入 Eve 的公钥而不是 Bob 的公钥)。验证可以通过她的包管理器、应用商店(大部分用于代码签名和 SSL/TLS 下载)或签名的可执行文件/源 tarball 来完成。

有关泄露私钥的说明,请参阅此答案的最后一部分。

防篡改分发

在客户端的用户以某种方式(使用某些用户特定的密码或将来可能使用 SQRL)进行身份验证后,您可以通过 HTTPS 在网站上提供 QR 码,显示客户端可以扫描的 QR 码或链接通过将自身注册到操作系统作为该 uri 协议的处理程序来自定义协议。

上述 QR 码 / uri 方法的一个不太复杂的替代方法是让用户从上述 HTTPS 站点复制粘贴一个共享密钥,该密钥将用于使用 sodium 的 crypto_secretbox() 方法进行初始公钥交换。

请注意,共享密钥/公钥在传输过程中可能应该是 base64、base32 或十六进制编码,因为密钥中的某些字节可能会被不同的字符编码破坏。

如果服务器私钥泄露怎么办?

如果您想在私钥泄漏的情况下进行前向保密,那么最初的公钥/私钥对应该只用于交换第二轮公钥,这些公钥永远不会保存到永久存储介质(本质上,没有磁盘或数据库) )。由于您的案例只是一个测试应用程序,因此前向保密可能不是很重要。

第二轮密钥每隔一段时间(如每 24 小时一次)被丢弃,从而降低了过去数据因 OpenSSL 的 Heartbleed 等漏洞而变得易受攻击的风险。

在使用 PHP 的传统网络服务器中,这可能很难做到,因为每个 HTTP 请求都会重置所有变量。正确配置的 memcached 实例可能是一种选择,但存在泄漏临时私钥的风险。

于 2014-04-08T23:23:23.900 回答