PHP Conference Japan 2024

openssl_dh_compute_key

(PHP 5 >= 5.3.0, PHP 7, PHP 8)

openssl_dh_compute_key計算遠端 DH 公開金鑰的公開值與本機 DH 金鑰的共享機密

描述

openssl_dh_compute_key(string $public_key, #[\SensitiveParameter] OpenSSLAsymmetricKey $private_key): string|false

openssl_dh_compute_key() 傳回的共享機密通常用作加密金鑰,以秘密地與遠端方通訊。這稱為 Diffie-Hellman 金鑰交換。

注意

遠端和本機金鑰組使用相同的 DH 參數非常重要;否則,雙方之間產生的機密將不匹配。

注意 僅從 PHP 8.1.0 OpenSSL 3.0.0 開始支援 ECDH。

參數

public_key

遠端方的 DH 公開金鑰。

private_key

本機 DH 私密金鑰,對應於將與遠端方共享的公開金鑰。

傳回值

成功時傳回共享機密,失敗時傳回 false

變更記錄

版本 描述
8.0.0 private_key 現在接受 OpenSSLAsymmetricKey;先前,接受 resource 型別為 OpenSSL key 的資源。

範例

範例 #1 計算共享機密

首先在本機產生公開/私密 DH 金鑰組,並讓遠端方執行相同的操作。我們需要使用 openssl 命令列公用程式。

# generate private/public key keypair
openssl dhparam -out dhparam.pem 2048
openssl genpkey -paramfile dhparam.pem -out privatekey.pem

# extract public key only
openssl pkey -in privatekey.pem -pubout -out publickey.pem

接下來,將您的公開金鑰傳送給遠端方。使用 openssl pkey 命令檢視您將從遠端方收到的公開金鑰。

openssl pkey -pubin -in remotepublickey.pem -text -noout

上述範例將輸出類似於

PKCS#3 DH Public-Key: (2048 bit)
    public-key:
        67:e5:e5:fa:e0:7b:0f:96:2c:dc:96:44:5f:50:02:
        9e:8d:c2:6c:04:68:b0:d1:1d:75:66:fc:63:f5:e3:
        42:30:b8:96:c1:45:cc:08:60:b4:21:3b:dd:ee:66:
        88:db:77:d9:1e:11:89:d4:5c:f2:7a:f2:f1:fe:1c:
        77:9d:6f:13:b8:b2:56:00:ef:cb:3b:60:79:74:02:
        98:f5:f9:8e:3e:b5:62:08:de:ca:8c:c3:40:4a:80:
        79:d5:43:06:17:a8:19:56:af:cc:95:5e:e2:32:2d:
        d2:14:7b:76:5a:9a:f1:3c:76:76:35:cc:7b:c1:a5:
        f4:39:e5:b6:ca:71:3f:7c:3f:97:e5:ab:86:c1:cd:
        0e:e6:ee:04:c9:e6:2d:80:7e:59:c0:49:eb:b6:64:
        4f:a8:f9:bb:a3:87:b3:3d:76:01:9e:2b:16:94:a4:
        37:30:fb:35:e2:63:be:23:90:b9:ef:3f:46:46:04:
        94:8f:60:79:7a:51:55:d6:1a:1d:f5:d9:7f:4a:3e:
        aa:ac:b0:d0:82:cc:c2:e0:94:e0:54:c1:17:83:0b:
        74:08:4d:5a:79:ae:ff:7f:1c:04:ab:23:39:4a:ae:
        87:83:55:43:ab:7a:7c:04:9d:20:80:bb:af:5f:16:
        a3:e3:20:b9:21:47:8c:f8:7f:a8:60:80:9e:61:77:
        36
 [...abbreviated...]

使用此公開金鑰作為 openssl_dh_compute_key() 的參數,以計算共享機密。

<?php
$remote_public_key
= '67e5e5fae07b0f962cdc96445f50029e8dc26c0468b0d11d7566fc63f5e34230b896c145cc0860b4213bddee6688db77d91e1189d45cf27af2f1fe1c779d6f13b8b25600efcb3b6079740298f5f98e3eb56208deca8cc3404a8079d5430617a81956afcc955ee2322dd2147b765a9af13c767635cc7bc1a5f439e5b6ca713f7c3f97e5ab86c1cd0ee6ee04c9e62d807e59c049ebb6644fa8f9bba387b33d76019e2b1694a43730fb35e263be2390b9ef3f464604948f60797a5155d61a1df5d97f4a3eaaacb0d082ccc2e094e054c117830b74084d5a79aeff7f1c04ab23394aae87835543ab7a7c049d2080bbaf5f16a3e320b921478cf87fa860809e617736';

$local_priv_key = openssl_pkey_get_private('file://privatekey.pem');

$shared_secret = openssl_dh_compute_key(hex2bin($remote_public_key), $local_priv_key);
echo
bin2hex($shared_secret)."\n";
?>

範例 #2 在 php 中產生 DH 公開/私密金鑰組

首先,產生 DH 質數

openssl dhparam -out dhparam.pem 2048
openssl dh -in dhparam.pem -noout  -text

上述範例將輸出類似於

PKCS#3 DH Parameters: (2048 bit)
        prime:
            00:a3:25:1e:73:3f:44:b9:2b:ee:f4:9d:9f:37:6a:
            4b:fd:1d:bd:f4:af:da:c8:10:77:59:41:c6:5f:73:
            d2:88:29:39:cd:1c:5f:c3:9f:0f:22:d2:9c:20:c1:
            e4:c0:18:03:b8:b6:d8:da:ad:3b:39:a6:da:8e:fe:
            12:30:e9:03:5d:22:ba:ef:18:d2:7b:69:f9:5b:cb:
            78:c6:0c:8c:6b:f2:49:92:c2:49:e0:45:77:72:b3:
            55:36:30:f2:40:17:89:18:50:03:fa:2d:54:7a:7f:
            34:4c:73:32:b6:88:14:51:14:be:80:57:95:e6:a3:
            f6:51:ff:17:47:4f:15:d6:0e:6c:47:53:72:2c:2a:
            4c:21:cb:7d:f3:49:97:c9:47:5e:40:33:7b:99:52:
            7e:7a:f3:52:27:80:de:1b:26:6b:40:bb:14:11:0b:
            fb:e6:d8:2f:cf:a0:06:2f:96:b9:1c:0b:b4:cb:d3:
            a6:62:9c:48:67:f6:81:f2:c6:ff:45:03:0a:9d:67:
            9d:ce:27:d9:6b:48:5d:ca:fb:c2:5d:84:9b:8b:cb:
            40:c7:a4:0c:8a:6e:f4:ab:ba:b6:10:c3:b8:25:4d:
            cf:60:96:f4:db:e8:00:1c:58:47:7a:fb:51:86:d1:
            22:d7:4e:94:31:7a:d5:da:3d:53:de:da:bb:64:8d:
            62:6b
        generator: 2 (0x2)

質數和產生器值作為 p 和 g 傳遞到 openssl_pkey_new()

<?php
$configargs
= array();
$configargs['p'] = hex2bin('00a3251e733f44b92beef49d9f376a4bfd1dbdf4afdac810775941c65f73d2882939cd1c5fc39f0f22d29c20c1e4c01803b8b6d8daad3b39a6da8efe1230e9035d22baef18d27b69f95bcb78c60c8c6bf24992c249e0457772b3553630f2401789185003fa2d547a7f344c7332b688145114be805795e6a3f651ff17474f15d60e6c4753722c2a4c21cb7df34997c9475e40337b99527e7af3522780de1b266b40bb14110bfbe6d82fcfa0062f96b91c0bb4cbd3a6629c4867f681f2c6ff45030a9d679dce27d96b485dcafbc25d849b8bcb40c7a40c8a6ef4abbab610c3b8254dcf6096f4dbe8001c58477afb5186d122d74e94317ad5da3d53dedabb648d626b');
$configargs['g'] = hex2bin('02');
$private_key = openssl_pkey_new(array('dh' => $configargs));
openssl_pkey_export_to_file($private_key,'privatekey.pem',$passphrase='y0urp@s5phr@se');

$details = openssl_pkey_get_details($private_key);
$local_pub_key = $details['dh']['pub_key'];
echo
bin2hex($local_pub_key)."\n";//您可以將您的公開金鑰傳送給遠端方

$details = openssl_pkey_get_details(openssl_pkey_get_public("file://remotepublickey.pem"));
$remote_public_key = $details['dh']['pub_key'];
$shared_secret = openssl_dh_compute_key($remote_public_key, $private_key);
echo
bin2hex($shared_secret)."\n";
?>

參見

新增註解

使用者貢獻的註解 4 則註解

k.s.swaminathan at live dot com
3 年前
// 目的:提供一個完全使用 PHP 實現 Diffie-Hellman 的範例。

// 這個函式產生 Diffie-Hellman 金鑰對的組態。
// 我們從一個空的組態開始,並讓 openssl_pkey_new 建立
// 一個質數和一個產生器。這是一個耗時的步驟。

function get_DH_params ($keylength=2048, $digest_alg="sha512")
{
$pkey = openssl_pkey_new(["digest_alg" => $digest_alg,
"private_key_bits" => $keylength,
"private_key_type" => OPENSSL_KEYTYPE_DH]);
$details = openssl_pkey_get_details($pkey);
return [
"digest_alg" => $digest_alg,
"private_key_bits" => $keylength,
"dh" => array('p' => $details['dh']['p'], 'g' => $details['dh']['g']),
"private_key_type" => OPENSSL_KEYTYPE_DH,
];
}

// 現在 Alice 和 Bob 可以建立他們各自的金鑰對
function get_DH_keyPair ($DH_params)
{
$pkey = openssl_pkey_new($DH_params);
$privkey = openssl_pkey_get_private($pkey);
$pubkey = openssl_pkey_get_details($pkey)['dh']['pub_key'];
return (object) compact('pubkey','privkey');
}

// 現在 Alice 和 Bob 可以建立一個共同的秘密金鑰
function get_DH_mutualsecret($peers_public, $my_private)
{
return bin2hex(openssl_dh_compute_key($peers_public, $my_private));
}

// 用法
>>> $dh_params = get_DH_params();
=> [
"digest_alg" => "sha512",
"private_key_bits" => 2048,
"dh" => [
"p" => b"ó»¸'#ð\x18\x04Û_Ä\tõyÁZàx\x15\x14\x11ƒ┬l=Ü┤H\0",
"g" => "\x02",
],
"private_key_type" => 2,
]

// Alice & Bob 從相同的 dh_params 產生他們的金鑰。
// 二進位值已截斷。

>>> $alice = get_DH_keypair($dh_params);
=> {#3773
+"pubkey": b"""EØüÔSðÔîË╚ùà5ÜLÜ$┘▄±ü6]",
+"privkey": OpenSSLAsymmetricKey {#3771},
}

>>> $bob = get_DH_keypair($dh_params);
=> {#3774
+"pubkey": b"'ua¥ªo\ê\x11║OM©\vó╣ßÜWöíþ³e÷:\t9Ô\rB┌\x13",
+"privkey": OpenSSLAsymmetricKey {#3765},
}

>>> $alice_secret = get_DH_mutualsecret($bob->pubkey, $alice->privkey);
=> "5fbf9df2f13da103f106. ....."

>>> $bob_secret = get_DH_mutualsecret($alice->pubkey, $bob->privkey);
=> "5fbf9df2f13da103f106. ....."

>>> $bob_secret == $alice_secret;
=> true

// 現在 Alice 和 Bob 有一個共享的秘密金鑰,他們可以將其用作對稱金鑰。金鑰的長度將為 2048 位元(與 DH 金鑰長度參數相同)。如果他們想的話,可以雜湊它以獲得更短的金鑰。

// 第三個人 Charlie 也可以像 Alice 和 Bob 一樣建立金鑰對。
// 而 Charlie 和 Alice 可以建立他們自己的,就像 Alice 和 Bob 做的那樣。
// 而 Charlie 和 Bob 可以建立他們自己的(獨立的)秘密金鑰。
//
vangelier at hotmail dot com
3 年前
一個可運作的範例。經過一些研究和閱讀,我終於了解此方法是如何運作的。

您需要遵循以下 4 個步驟:

1. 您建立一個 1:n 方都知道的公鑰。
2. 每方建立自己的金鑰對。
2a. 每方與成員分享他們的公鑰。
3. 每位使用者可以使用自己的私鑰和其他方的公鑰重新建立共享秘密金鑰。
4. 比較秘密金鑰作為握手。

/* 1. 建立第一個,全域已知的公鑰。*/

/**
* 取得 DH 公開/私密金鑰
* @return array
*/
public static function get_keypair()
{
$keys = [];

$config = [
"digest_alg" => "sha512",
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_DH,
];

// 建立私密金鑰和公鑰
$res = openssl_pkey_new($config);

$pubKey = openssl_pkey_get_details($res);
$keys["public"] = $pubKey["key"];

openssl_pkey_export($res, $privKey);

$keys["private"] = $privKey;

return $keys;
}

現在您與群組的每位成員分享公鑰。

/* 2. 每位使用者使用來自全域公鑰資訊的 P,G 建立新的金鑰對 */

$key = openssl_get_publickey(base64_decode($publicKey));
$info = openssl_pkey_get_details($key);
$params = $info["dh"];

現在您有了來自公鑰的 P,G。使用它;

/**
* 從質數和產生器建立金鑰交換的金鑰對
* @param $prime
* @param $generator
*/
public static function create_keypair_from_pg($prime, $generator)
{
$config = [
"digest_alg" => "sha512",
"private_key_bits" => 2048,
"dh" => [
"p" => $prime,
"g" => $generator
],
"private_key_type" => OPENSSL_KEYTYPE_DH,
];

return openssl_pkey_new($config);
}

/* 3. 使用您的私鑰和使用者 1:n 的公鑰建立共享秘密金鑰 */

$privateKey = openssl_get_publickey(base64_decode($privateKeyData));

$secret1 = openssl_dh_compute_key($user1PublicKey, $privateKey);
if($secret !== false) {
return bin2hex($secret);
}else{
print_r(openssl_error_string());
}

$secret2 = openssl_dh_compute_key($user2PublicKey, $privateKey);
if($secret !== false) {
return bin2hex($secret);
}else{
print_r(openssl_error_string());
}

/* 4. 比較秘密金鑰作為握手方法 */

if(strcmp($secret1, $secret2) === 0) {
return true;
}

return false;

祝您好運,享受!。請隨時通知我有關改進和更新。vangelier AT hotmail DOT com
vangelier at hotmail dot com
3 年前
是否有人可以張貼一個可運作的範例?我寫了許多測試和範例,但我似乎無法使用此方法取得 2 個相同的秘密金鑰。

我正在遵循這個:https://sandilands.info/sgordon/diffie-hellman-secret-key-exchange-with-openssl

在控制台中,它運作良好。使用 openssl_dh_compute_key 它無法運作。
vangelier at hotmail dot com
3 年前
經過一些挑戰,我決定編寫 C++ 和 PHP 程式碼範例。

由於很難掌握 Diffie 和 Hellman 演算法是如何運作的。這些程式碼範例是跨相容的。

包含 PHP 程式碼和 C++ 程式碼的 Gist

https://gist.github.com/digitalhuman/2a2b85d61672e4bf83596d41351723ba

享受!
To Top