使用帶有 crypto_sign
的 LibSodium CryptoBox 密鑰對
更多的理論問題……
以一個使用 Lib Sodium 進行非對稱加密的系統為例,其中已生成“crypto_box_keypair”,並且公鑰已分發到許多不同的系統。
是否可以使用該密鑰對簽署消息?
例如,消息以純文字形式保存,並使用密鑰(可能間接)生成簽名,任何擁有(並信任)公鑰的人都可以驗證消息沒有被篡改。
考慮到“crypto_sign”功能需要使用“crypto_sign_keypair”,而不是“crypto_box_keypair”。
從 PHP 實現的角度來看,我想知道您是否可以…
獲取先前由以下方式生成的密鑰對:
<?php $key_pair = sodium_crypto_box_keypair(); $key_secret = sodium_crypto_box_secretkey($key_pair); $key_public = sodium_crypto_box_publickey($key_pair); ?>
然後,對於您要簽名的每條消息,創建一個“crypto_sign_keypair”,並按
sodium_crypto_sign()
預期使用:<?php // $sign_seed = random_bytes(SODIUM_CRYPTO_SIGN_SEEDBYTES); // $sign_pair = sodium_crypto_sign_seed_keypair($sign_seed); $sign_pair = sodium_crypto_sign_keypair(); $sign_secret = sodium_crypto_sign_secretkey($sign_pair); $sign_public = sodium_crypto_sign_publickey($sign_pair); $message = 'Hello'; $message_signed = sodium_crypto_sign($message, $sign_secret); ?>
$sign_secret
and可以被銷毀,$sign_pair
因為接收者只需要使用$sign_public
:<?php $message = sodium_crypto_sign_open($message_signed, $sign_public); ?>
問題是
$sign_public
需要發送給收件人。他們需要
$key_secret
通過使用受信任的$key_public
.注意到:
sodium_crypto_sign_detached
基本上是同一件事,但在其輸出中不包含消息。sodium_crypto_box
要求收件人也有一個密鑰對。sodium_crypto_box_seal
使用公鑰進行加密。sodium_crypto_secretbox
並sodium_crypto_aead_*
使用共享密鑰(對稱)。sodium_crypto_auth
使用共享密鑰。
可以反過來(感謝Scott Arciszewski)
<?php $sign_pair = sodium_crypto_sign_keypair(); $sign_secret = sodium_crypto_sign_secretkey($sign_pair); $sign_public = sodium_crypto_sign_publickey($sign_pair); $key_secret = sodium_crypto_sign_ed25519_sk_to_curve25519($sign_secret); $key_public = sodium_crypto_sign_ed25519_pk_to_curve25519($sign_public); $key_public = sodium_crypto_box_publickey_from_secretkey($key_secret); ?>
對於兩個(過於復雜的)解決方案,從 crypto_box_keypair 開始:
<?php $key_pair = sodium_crypto_box_keypair(); $key_secret = sodium_crypto_box_secretkey($key_pair); $key_public = sodium_crypto_box_publickey($key_pair); ?>
並使用一次性密鑰對;每個人都可以看到這個密鑰對的公共和私有部分(這似乎有點奇怪)……
版本 1,仍然使用
sodium_crypto_sign()
,以及sodium_crypto_box()
“加密”公共簽名密鑰(任何人都可以解密,但它會驗證它沒有被篡改)。我們用以下方式簽署消息:
<?php function sign($key_secret, $message) { $sign_pair = sodium_crypto_sign_keypair(); // Different to sodium_crypto_box_keypair() $sign_secret = sodium_crypto_sign_secretkey($sign_pair); $sign_public = sodium_crypto_sign_publickey($sign_pair); $message_signed = sodium_crypto_sign($message, $sign_secret); $sign_box_secret = sodium_crypto_sign_ed25519_sk_to_curve25519($sign_secret); // Convert crypto_sign_keypair to crypto_box_keypair. $sign_box_public = sodium_crypto_sign_ed25519_pk_to_curve25519($sign_public); $sign_box_nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES); $sign_box_pair = sodium_crypto_box_keypair_from_secretkey_and_publickey($key_secret, $sign_box_public); $sign_box_encrypted = sodium_crypto_box($sign_public, $sign_box_nonce, $sign_box_pair); return [$message_signed, $sign_box_encrypted, $sign_box_nonce, $sign_box_secret]; } $package = sign($key_secret, 'hello'); ?>
然後驗證並提取消息:
<?php function verify($key_public, $package) { list($message_signed, $sign_box_encrypted, $sign_box_nonce, $sign_box_secret) = $package; $sign_box_pair = sodium_crypto_box_keypair_from_secretkey_and_publickey($sign_box_secret, $key_public); $sign_public = sodium_crypto_box_open($sign_box_encrypted, $sign_box_nonce, $sign_box_pair); return sodium_crypto_sign_open($message_signed, $sign_public); } echo verify($key_public, $package); ?>
版本 2,我們可以跳過
sodium_crypto_sign()
,因為sodium_crypto_box()
允許我們在解密期間驗證消息。我們用以下方式簽署消息:
<?php function sign($key_secret, $message) { $shared_pair = sodium_crypto_box_keypair(); $shared_secret = sodium_crypto_box_secretkey($shared_pair); $shared_public = sodium_crypto_box_publickey($shared_pair); $nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES); $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($key_secret, $shared_public); $message_signed = sodium_crypto_box($message, $nonce, $keypair); return [$message_signed, $shared_secret, $nonce]; } $package = sign($key_secret, 'hello'); ?>
然後驗證並提取消息:
<?php function verify($key_public, $package) { list($message_signed, $shared_secret, $nonce) = $package; $keypair = sodium_crypto_box_keypair_from_secretkey_and_publickey($shared_secret, $key_public); return sodium_crypto_box_open($message_signed, $nonce, $keypair); } echo verify($key_public, $package); ?>
第一個版本更複雜,但它確實更接近於簽名的原始概念,因為消息仍然是純文字……你可以看到:
echo substr($package[0], SODIUM_CRYPTO_SIGN_BYTES);