openssl_dh_compute_key

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

openssl_dh_compute_keyComputes shared secret for public value of remote DH public key and local DH key

Description

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

The shared secret returned by openssl_dh_compute_key() is often used as an encryption key to secretly communicate with a remote party. This is known as the Diffie-Hellman key exchange.

Caution

It is important to use the same DH parameters for remote and local key pairs; otherwise, the generated secret between the two parties will not match.

Note: ECDH is only supported as of PHP 8.1.0 and OpenSSL 3.0.0.

Parameters

public_key

DH Public key of the remote party.

private_key

A local DH private key, corresponding to the public key to be shared with the remote party.

Return Values

Returns shared secret on success or false on failure.

Changelog

Version Description
8.0.0 private_key accepts an OpenSSLAsymmetricKey now; previously, a resource of type OpenSSL key was accepted.

Examples

Example #1 Compute a shared secret

First generate a public/private DH keypair locally, and have the remote party do the same. We need to use the openssl command-line utility.

# 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

Next, send your public key to the remote party. Use the openssl pkey command to view the public key you will be sent from the remote party.

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

The above example will output something similar to:

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...]

Use this public key as a parameter to openssl_dh_compute_key() in order to compute the shared secret.

<?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";
?>

Example #2 Generate a DH public/private keypair in php

First, generate the DH prime number

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

The above example will output something similar to:

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)

Prime and generator values ares passed as p and g into 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";//you can send your public key to the remote party

$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";
?>

See Also

add a note

User Contributed Notes 4 notes

up
1
k.s.swaminathan at live dot com
3 years ago
// Purpose: Provide a working example of Diffie-Hellman, entirely in php.

// This function generates a configuration for Diffie-Hellman keypair
// We start with an empty config and have openssl_pkey_new create
// a prime and a generator. This is a time consuming step.

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,
];
}

// Now Alice and Bob can create their respective keypairs
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');
}

// Now Alice and Bob can create a mutual secret
function get_DH_mutualsecret($peers_public, $my_private)
{
return bin2hex(openssl_dh_compute_key($peers_public, $my_private));
}

// Usage
>>> $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 generate their keys from the same dh_params.
// Binary values truncated.

>>> $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

// Now Alice and Bob have a shared secret which they can use as a symmetric key. The key will be 2048 bits long (same as the DH key length parameter). They can hash it to get a shorter key if they want.

// A third person, Charlie, can also create a key pair like Alice and Bob.
// And Charlie and Alice can create their own Alice and Bob did.
// And Charlie and Bob can create their own (separate) secret.
//
up
0
vangelier at hotmail dot com
4 years ago
A working example. After some study and reading I finally get how this method is working.

You need to follow the below 4 steps;

1. You create a public key which is known to 1:n parties.
2. Each party creates their own keypair.
2a. Each party shared their public key with the members.
3. Each user can re-create the shared secret by using his Private Key and the Public Key of the other parties.
4. Compare the secrets as a handshake

/* 1. Create the first, global known public key. */

/**
* Get DH public/private keys
* @return array
*/
public static function get_keypair()
{
$keys = [];

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

// Create the private and public key
$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;
}

Now you share the Public Key with every member of the party.

/* 2. Each user creates a new Key Pair with the P,G from the global public key info */

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

Now you have the P,G from the public key. Use it;

/**
* Create keypair from Prime and Generator for KeyExchange
* @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. Create a shared secret with your Private Key, and User 1:n's Public Key */

$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. Compare the secrets as a handshake method */

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

return false;

Good luck, enjoy!. Keep me posted about improvements and updates. vangelier AT hotmail DOT com
up
0
vangelier at hotmail dot com
4 years ago
Is it possible for someone to post a working example? I have written many test and examples, and I just can't seem to get 2 secrets that are alike with this method.

I am following this; https://sandilands.info/sgordon/diffie-hellman-secret-key-exchange-with-openssl

With the console, it works great. With openssl_dh_compute_key it does not work.
up
-1
vangelier at hotmail dot com
3 years ago
After some challenges I decided to write a C++ and PHP code samples.

As it can be very tricky to get a grib on how the Diffie and Hellman algoritm work. The code samples are cross compatible.

Gist with PHP code and C++ code:

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

Enjoy!
To Top