openssl_dh_compute_key():参数 #2 ($private_key) 必须是 OpenSSLAsymmetricKey 类型
openssl_dh_compute_key(): Argument #2 ($private_key) must be of type 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
public和私钥的内容:
# cat public
{"curve25519_public_key":"ru7+Ncx9x8Y/pHlj3D/wg+WYCQgqMKuVpAmTjCmf7Hs="}
# cat private
9fd06c52f7fda2b89bb05ac591dbeba8e57984c2bb0e6181ddcf275fb87015b6
所以我的猜测是 openssl_pkey_get_private
函数无法读取私钥格式。我的问题是如何在 openssl_dh_compute_key
?
中使用这些私钥和 public 密钥
有关更多上下文,下面是我如何使用 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 加密令牌
然后我 运行 Vault Agent 得到以下响应 ("token.txt"):
{
"curve25519_public_key": "ONwU5iknsVP57p7PdHtN4rzxbivMB4Bt5o2BJFC6oSc=",
"nonce": "AA2VibL5SXE9MU19",
"encrypted_payload": "pPseQIue1IXxutEKoZrjsORj+8AZVgRrcTRvaPVxukzU2w28TL4T0be7aGFKZmHwudiHiNQyp5i8D0ZUgP/ILLYPfhO+gwxUFEDhA4PJNAgKc8nSaMpjG9RipAyRcepgk42SXuRIgZ1D7HrmeWT8"
}
有关详细信息,请查看 https://www.vaultproject.io/docs/agent/autoauth#encrypting-tokens
解密令牌
然后我解密响应如下
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
可以在 PHP 中使用 Sodium library and the sodium_crypto_scalarmult()
函数确定共享秘密。
对于共享密钥的计算,DeriveSharedKey()
shows that the shared key is derived with HKDF (see hkdf.go) and the two public keys are sorted and used as salt and info. For this purpose hash_hkdf()
的 Go 源代码可以在 PHP 端使用。
最后,在 GCM 模式下使用 AES-256 进行解密,例如openssl_encrypt()
。与将密文和标签连接在一起的 Go 代码不同,openssl_encrypt()
将两者分开处理。
得到的PHP代码为(为简单起见,输入数据直接赋值,即没有文件I/O):
// 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 代码的输出相同。
我正在尝试解密响应令牌,其中使用 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
public和私钥的内容:
# 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 加密令牌
然后我 运行 Vault Agent 得到以下响应 ("token.txt"):
{
"curve25519_public_key": "ONwU5iknsVP57p7PdHtN4rzxbivMB4Bt5o2BJFC6oSc=",
"nonce": "AA2VibL5SXE9MU19",
"encrypted_payload": "pPseQIue1IXxutEKoZrjsORj+8AZVgRrcTRvaPVxukzU2w28TL4T0be7aGFKZmHwudiHiNQyp5i8D0ZUgP/ILLYPfhO+gwxUFEDhA4PJNAgKc8nSaMpjG9RipAyRcepgk42SXuRIgZ1D7HrmeWT8"
}
有关详细信息,请查看 https://www.vaultproject.io/docs/agent/autoauth#encrypting-tokens
解密令牌
然后我解密响应如下
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
可以在 PHP 中使用 Sodium library and the sodium_crypto_scalarmult()
函数确定共享秘密。
对于共享密钥的计算,DeriveSharedKey()
shows that the shared key is derived with HKDF (see hkdf.go) and the two public keys are sorted and used as salt and info. For this purpose hash_hkdf()
的 Go 源代码可以在 PHP 端使用。
最后,在 GCM 模式下使用 AES-256 进行解密,例如openssl_encrypt()
。与将密文和标签连接在一起的 Go 代码不同,openssl_encrypt()
将两者分开处理。
得到的PHP代码为(为简单起见,输入数据直接赋值,即没有文件I/O):
// 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 代码的输出相同。