我试图理解使用 PHP 的钠加密盒共享加密消息。 使用我在某处找到的一个示例,我尝试模拟两个人之间的对话。他们每个人都在本地系统上创建了自己的公共/私有密钥集。
发送者使用以下方式创建共享密钥:
sodium_crypto_box_keypair_from_secretkey_and_publickey(Sender's Private Key, Recipient's Public Key)
发件人然后使用以下方式打包消息:
sodium_crypto_box($message, nonce, Sender's keypair)
根据我的阅读,这对消息进行签名以进行身份验证,然后对消息进行加密以进行安全传输。在接收端,接收者使用他的私钥和发送者的公钥创建他自己的密钥集。然后他使用本地按键组打开盒子。
sodium_crypto_box_open($encrypted_signed_text, nonce, Recipient's keypair)
这一切看起来都很好。安全通信成为可能。或者是吗?
通过剪切和粘贴错误,我发现发件人也可以解密加密的消息。
sodium_crypto_box_open($encrypted_signed_text, nonce, Sender's keypair)
如果无法访问接收者的私钥,这怎么可能?
幕后到底发生了什么?我是否正确使用了这个功能?我读过的所有内容都说我们不应该推出自己的加密函数,因为它们尚未在数学上被证明是加密安全的。这个过程加密安全吗?这个密钥集的构建是否已经被加密社区中那些真正知道自己在做什么的人从数学上证明了,而不是像我这样只是将别人的工作拼凑在一起而没有真正理解它的人?诚然,我将要传达的内容不值得任何人花时间去破解,但就其原理而言,我仍然想把它做好。
我的代码:
<?php
//===============================================================
//This is another Puplic Key Example with sender authentication
// On Alice's device
$alice_keypair = sodium_crypto_box_keypair();
$alice_secret_key = sodium_crypto_box_secretkey($alice_keypair);
$alice_public_key = sodium_crypto_box_publickey($alice_keypair);
// On Bob's device
$bob_keypair = sodium_crypto_box_keypair();
$bob_secret_key = sodium_crypto_box_secretkey($bob_keypair);
$bob_public_key = sodium_crypto_box_publickey($bob_keypair);
// Exchange keys:
// - Send Alice's public key to Bob.
// - Send Bob's public key to Alice.
// On sender:
// Create nonce. Convert to Hex so it may be easily stored and transmitted
$hexnonce = sodium_bin2hex(random_bytes(\SODIUM_CRYPTO_BOX_NONCEBYTES));
// Create enc/sign key pair.
$sender_keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($alice_secret_key, $bob_public_key);
$message = "New message: Hi Bob, I'm Alice";
// Encrypt and sign the message with the sender's keyset
//Here I assume the function is using the sender's private key portion to sign the message
//and using the recipient's public key portion to encrypt the message
$hexencrypted_signed_text = sodium_bin2hex(sodium_crypto_box($message, sodium_hex2bin($hexnonce), $sender_keypair));
//Here the message and nonce are transmitted to the recipient...
// On recipient:
$recipient_keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($bob_secret_key, $alice_public_key);
// Authenticate and decrypt message
//Here I assume the function is using the Recipient's private key portion of the keyset to decrypt the message
//and using the sender's public key portion to authenticate that the message was signed by Alice.
$NewOrigMsg = sodium_crypto_box_open(sodium_hex2bin($hexencrypted_signed_text), sodium_hex2bin($hexnonce), $recipient_keypair);
echo "<br>===The Original message===<br>";
var_dump($message); // "Hi Bob, I'm Alice"
echo "<br><br>===Transmitted Values===<br>";
echo "Nonce: " . $hexnonce . "<br>";
echo "Encrypted Message: " . $hexencrypted_signed_text . "<br>";
echo "<br>=== Recipient's Decrypted Version===<br>";
var_dump($NewOrigMsg); // "Hi Bob, I'm Alice"
//Notice that the sender can decrypt the message using the original encryption keypair
//I would expect that this would not be possible since the sender does not have access to the Recipient's private key
$orig_msg = sodium_crypto_box_open(sodium_hex2bin($hexencrypted_signed_text), sodium_hex2bin($hexnonce), $sender_keypair);
echo "<br><br>===Senders's Decrypted Version===<br>";
var_dump($orig_msg); // "Hi Bob, I'm Alice"
//==============================================================
?>
我尝试通过为当前项目构建一个带有我自己的调整的页面来验证我对网上找到的示例的理解。
正如您自己在评论中已经认识到的那样,您误解了 crypto_box 概念:在发送方,既不使用接收方的公钥进行加密,也不使用发送方的密钥进行签名,在接收方也不使用接收者的密钥用于解密,发送者的公钥用于验证。
crypto_box是一种公钥验证的加密。它使用 X25519 进行密钥协商,使用 XSalsa20-Poly1305 进行身份验证加密。
对于加密,由发送者的秘密密钥和接收者的公钥组成的组合 K(secretS, publicR) 用于使用 X25519 派生共享秘密。
同样,在接收方,在解密过程中使用由接收方密钥和发送方公钥组成的组合K(secretR, publicS),以使用 X25519 导出共享密钥。
X25519 算法确保以这种方式生成的两个共享密钥是相同的。
Libsodium 为 X25519 提供了方法
sodium_crypto_scalarmult()
来生成共享密钥,可以用来演示这一点:
<?php
$sender_keypair = sodium_crypto_box_keypair();
$sender_secret_key = sodium_crypto_box_secretkey($sender_keypair);
$sender_public_key = sodium_crypto_box_publickey($sender_keypair);
$receiver_keypair = sodium_crypto_box_keypair();
$receiver_secret_key = sodium_crypto_box_secretkey($receiver_keypair);
$receiver_public_key = sodium_crypto_box_publickey($receiver_keypair);
$sender_shared_secret = sodium_crypto_scalarmult($sender_secret_key, $receiver_public_key);
$receiver_shared_secret = sodium_crypto_scalarmult($receiver_secret_key, $sender_public_key);
print(strcmp($sender_shared_secret, $receiver_shared_secret) == 0 ? "match" : "no match"); // match
?>
双方生成相同的共享密钥后,双方从中派生出相同的密钥(使用 HSalsa20),该密钥用于使用 XSalsa20-Poly1305 进行身份验证加密。请注意,所描述的所有处理都包含在 crypto_box 中(即,这些都不需要由您自己完成)。
顺便说一句,XSalsa20-Poly1305 也是 Libsodium 的密钥认证加密crypto_secretbox使用的算法。
通常的 crypto_box 用例是发送者使用 K(secretS, publicR) 加密,接收者使用 K(secretR, publicS) 解密。
当然,用 K(secretS, publicR) 解密也是可能的,但这仅与发送方相关,因为只有它拥有发送方的密钥。
这适用于您最后的解密:发送方使用 K(secretS, publicR) 进行解密。顺便说一句,这不是一个安全问题,因为它只意味着发送者可以解密自己的密文。