与lib钠安全密钥交换


secure key exchange with libsodium

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

有许多语言的许多端口:c#,PHP,…

总有一个例子是"bob"answers"alice"。这很好,但是他们从来没有展示如何以安全的方式在网络上交换公钥。

那么,建议如何交换"alice/client"answers"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);

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

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

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

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

如果你在控制客户端

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

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

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

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

中间有个男人怎么样?

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

在Bob的眼里,她只是另一个客户。在Alice的眼中,她会感到沮丧,因为Bob似乎没有回应。

如果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或十六进制编码,因为密钥中的一些字节可能会被不同的字符编码弄乱。

服务器私钥泄露怎么办?

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

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

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