Libsodium

使用帶有 crypto_sign 的 LibSodium CryptoBox 密鑰對

  • December 24, 2018

更多的理論問題……

以一個使用 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_secretand可以被銷毀,$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_secretboxsodium_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);

引用自:https://crypto.stackexchange.com/questions/61757