我想做一个使用 libsodium 从客户端到服务器进行通信的测试应用程序。
并且总是有一个例子" 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);
答案 0 :(得分:9)
Libsodium不提供任何直接密钥交换方法。您可以通过几种不同的方式安全地进行密钥交换:
通过防篡改"带外通信"提供公钥或者如果通道对听众是安全的,则是共享秘密。 (例如BittorrentSync,包含从设备到设备扫描的共享密钥的QR码)
让受信任的第三方在未加密发送公钥之前对其进行签名。 (例如SSL / TLS中的证书颁发机构)
证书固定(例如Google Chrome for Google网址)
由于您可能不想重新实施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实例可能是一个选项,但存在泄漏临时私钥的风险。