与libsodium保持密钥交换

时间:2014-03-21 07:05:03

标签: php cryptography libsodium nacl-cryptography

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

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

并且总是有一个例子" bob"和"爱丽丝"。 这很好,但他们从未说明如何通过网络以安全方式交换公钥。

那么如何推荐交换" 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);

1 个答案:

答案 0 :(得分:9)

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

  1. 通过防篡改"带外通信"提供公钥或者如果通道对听众是安全的,则是共享秘密。 (例如BittorrentSync,包含从设备到设备扫描的共享密钥的QR码)

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

  3. 证书固定(例如Google Chrome for Google网址)

  4. 如果您掌控客户

    由于您可能不想重新实施SSL / TLS,因此第一个或第三个选项可能是最佳选择。然而,第一种在客户端/服务器格式上有点难,因为PHP服务器可能只有互联网进行通信。

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

    例如,Alice使用Bob的公钥和她自己的私钥来加密某些数据,并将其(以及唯一的随机数和Alice的公钥)一起发送给Bob。

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

    中间的男人怎么样?

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

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

    如果Eve发送Alice消息,他们将永远被标记为无效,因为Alice使用Alice的私钥和Bob的公钥来尝试解密它们,因为Eve没有Bob'私钥。

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

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

    防篡改分发

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

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

    请注意,共享密钥/公钥在传输过程中可能应该是base64,base32或hex编码,因为密钥中的某些字节可能会因不同的字符编码而受到损坏。

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

    如果在私钥泄漏的情况下需要forward secrecy,则初始公钥/私钥对仅应用于交换从未保存到永久存储介质的第二轮公钥(在本质,没有磁盘或数据库)。由于您的案例只是一个测试应用程序,因此Forward Secrecy可能不是很重要。

    第二轮密钥每隔一段时间(例如每24小时)就会被丢弃,从而降低过去数据由于OpenSSL的Heartbleed等漏洞而变得脆弱的风险。

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