Openssl_dh_compute_key(): Аргумент №2 ($private_key) должен иметь тип OpenSSLAsymmetricKey.

Я пытаюсь расшифровать токены ответа, которые были зашифрованы с использованием Diffie-Hellman (типа curve25519) через AES-GCM и дополнительные аутентифицированные данные (AAD). Я новичок в PHP, поэтому я не уверен, как это можно сделать.

вот моя попытка:

<?php


$private_key =  openssl_pkey_get_private("file:///vault-sink/private");

$pub_key_struct =  file_get_contents("/vault-sink/public");
$json_public_key = json_decode($pub_key_struct);


$secret = openssl_dh_compute_key($json_public_key->curve25519_public_key, $private_key);

$token_response =  file_get_contents("/vault-sink/token.txt");
$token_response_json = json_decode($token_response);

echo $token_response_json->encrypted_payload;

openssl_decrypt(
    $secret,
    $token_response_json->encrypted_payload,
    $token_response_json->nonce,
    $aad = "test"
);

ошибка

PHP Fatal error:  Uncaught TypeError: openssl_dh_compute_key(): Argument #2 ($private_key) must be of type OpenSSLAsymmetricKey, bool given in /vault-sink/php-client/main.php:10

содержимое открытых и закрытых ключей:

# cat public
{"curve25519_public_key":"ru7+Ncx9x8Y/pHlj3D/wg+WYCQgqMKuVpAmTjCmf7Hs="}

# cat private
9fd06c52f7fda2b89bb05ac591dbeba8e57984c2bb0e6181ddcf275fb87015b6

Итак, я предполагаю, что формат закрытого ключа не читается функцией openssl_pkey_get_private. Мой вопрос: как я могу использовать эти закрытые и открытые ключи в openssl_dh_compute_key?

Для большего контекста ниже приведен пример того, как я сделал это с помощью go.


Рабочий пример с Go

Создать пару ключей

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "os"

    dh "github.com/hashicorp/vault/helper/dhutil"
)

func check(e error) {
    if e != nil {
        log.Fatal(e)
    }
}

func generatePublicPrivateKey() {

    var keyinfo dh.PublicKeyInfo

    public, private, err := dh.GeneratePublicPrivateKey()
    check(err)

    keyinfo.Curve25519PublicKey = public
    pubkey, err := json.Marshal(keyinfo)
    check(err)

    err = os.WriteFile("public", []byte(pubkey), 0644)
    if err != nil {
        log.Fatal(err)
    }
    privateKey := fmt.Sprintf("%x", private)

    err = os.WriteFile("private", []byte(privateKey), 0644)
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    generatePublicPrivateKey()
}

выход

# cat public
{"curve25519_public_key":"ru7+Ncx9x8Y/pHlj3D/wg+WYCQgqMKuVpAmTjCmf7Hs="}

# cat private
9fd06c52f7fda2b89bb05ac591dbeba8e57984c2bb0e6181ddcf275fb87015b6

Зашифровать токен с помощью хранилища Hashicorp

затем я запускаю Vault Agent и получаю следующий ответ ("token.txt"):

{
    "curve25519_public_key": "ONwU5iknsVP57p7PdHtN4rzxbivMB4Bt5o2BJFC6oSc=",
    "nonce": "AA2VibL5SXE9MU19",
    "encrypted_payload": "pPseQIue1IXxutEKoZrjsORj+8AZVgRrcTRvaPVxukzU2w28TL4T0be7aGFKZmHwudiHiNQyp5i8D0ZUgP/ILLYPfhO+gwxUFEDhA4PJNAgKc8nSaMpjG9RipAyRcepgk42SXuRIgZ1D7HrmeWT8"
}

для получения дополнительной информации проверьте https://www.vaultproject.io/docs/agent/autoauth#encrypting-токены

Расшифровать токен

а затем я расшифровываю ответ следующим образом

package main

import (
    "encoding/hex"
    "encoding/json"
    "fmt"
    "log"
    "os"

    dh "github.com/hashicorp/vault/helper/dhutil"
)

func check(e error) {
    if e != nil {
        log.Fatal(e)
    }
}

func main() {

    var envelope dh.Envelope
    var keyinfo dh.PublicKeyInfo

    token_env, err := os.ReadFile("token.txt")
    check(err)

    err = json.Unmarshal(token_env, &envelope)
    check(err)

    ourPublic, err := os.ReadFile("public")
    check(err)

    ourPrivate, err := os.ReadFile("private")
    check(err)

    json.Unmarshal(ourPublic, &keyinfo)

    privatekey, err := hex.DecodeString(string(ourPrivate))
    check(err)

    secret, err := dh.GenerateSharedSecret(privatekey, envelope.Curve25519PublicKey)
    check(err)

    shared_key, err := dh.DeriveSharedKey(secret, keyinfo.Curve25519PublicKey, envelope.Curve25519PublicKey)
    check(err)

    test, err := dh.DecryptAES(shared_key, envelope.EncryptedPayload, envelope.Nonce, []byte("test"))
    check(err)

    fmt.Println(string(test))
}

выход

hvs.CAESIB1eP79cHXPc4ttZIz20qL2I2A8kcfNpkhPjvIE4DZt3Gh4KHGh2cy4wNWV1WHB5NWNuZXV1dDMzdkh4YkY2OUE
openssl_pkey_get_private() ожидает закрытый ключ в формате PEM, а не какой-либо другой.
Sammitch 17.05.2022 21:32
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Что нового в PHP 8.1?
Что нового в PHP 8.1?
Если вы все еще используете PHP 7, то эта статья для вас. В PHP 8, а именно в PHP 8.1, встроены некоторые очень востребованные функции, которые вам...
Разработка LMS на заказ для повышения эффективности работы и обучения
Разработка LMS на заказ для повышения эффективности работы и обучения
За последние годы в образовании произошла большая революция, и сегодня почти все учебные заведения делают упор на эксклюзивное управление учебным...
1
1
20
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Общий секрет можно определить в PHP с помощью библиотеки натрий и функции sodium_crypto_scalarmult().

Для расчета общего ключа исходный код Go DeriveSharedKey() показывает, что общий ключ получен с помощью ХКДФ (см. hkdf.go), а два открытых ключа отсортированы и используются как соль и Информация. Для этого на стороне PHP можно использовать hash_hkdf().

Наконец, дешифрование происходит с помощью AES-256 в режиме GCM, например. с openssl_encrypt(). В отличие от кода Go, где зашифрованный текст и тег объединены, openssl_encrypt() обрабатывает их по отдельности.

Результирующий PHP-код (для простоты входные данные назначаются напрямую, т.е. без файлового ввода-вывода):

// Input data
$payload = base64_decode("pPseQIue1IXxutEKoZrjsORj+8AZVgRrcTRvaPVxukzU2w28TL4T0be7aGFKZmHwudiHiNQyp5i8D0ZUgP/ILLYPfhO+gwxUFEDhA4PJNAgKc8nSaMpjG9RipAyRcepgk42SXuRIgZ1D7HrmeWT8");
$nonce = base64_decode("AA2VibL5SXE9MU19");
$ourPublicKey = base64_decode("ru7+Ncx9x8Y/pHlj3D/wg+WYCQgqMKuVpAmTjCmf7Hs=");
$theirPublicKey = base64_decode("ONwU5iknsVP57p7PdHtN4rzxbivMB4Bt5o2BJFC6oSc=");
$privateKey = hex2bin("9fd06c52f7fda2b89bb05ac591dbeba8e57984c2bb0e6181ddcf275fb87015b6");

// Get shared secret
$sharedSecret = sodium_crypto_scalarmult($privateKey, $theirPublicKey);
print("Shared secret (hex): " . sodium_bin2hex($sharedSecret) . PHP_EOL); // Shared secret (hex): 48cb642fe6ecd7e4ff4a58610524f873e8ab86b8ccb195f0c90d59c477d6f437

// Get shared key
// Go:  hkdf.New(hash, secret, salt, info)
// PHP: hash_hkdf($algo, $key, $length, $info, $salt)
if (strcmp($ourPublicKey, $theirPublicKey) == -1) {
    $salt = $ourPublicKey;
    $info = $theirPublicKey;
} else { // for simplicity the 0 case is not considered dedicated, the Go code throws an exception here
    $salt = $theirPublicKey;
    $info = $ourPublicKey;
}
$sharedKey = hash_hkdf("sha256", $sharedSecret, 32, $info, $salt);
print("Shared key (hex): " . bin2hex($sharedKey) . PHP_EOL); // Shared key (hex): cacbda4874426b1208903d24378bdc0fb7a7dd08c91b5f275c11ff39e58add38

// Decrypt with AES-256, GCM
$ciphertext = substr($payload, 0, strlen($payload) - 16);
$tag = substr($payload, strlen($payload) - 16, 16);
$aad = "test";
$plaintext = openssl_encrypt($ciphertext, "aes-256-gcm", $sharedKey, OPENSSL_RAW_DATA, $nonce, $tag, $aad);
print("Plaintext: " . $plaintext . PHP_EOL); // Plaintext: hvs.CAESIB1eP79cHXPc4ttZIz20qL2I2A8kcfNpkhPjvIE4DZt3Gh4KHGh2cy4wNWV1WHB5NWNuZXV1dDMzdkh4YkY2OUE

с тем же результатом, что и код Go.

блестяще, большое спасибо!

Kingindanord 18.05.2022 01:01

Другие вопросы по теме