# Each side: a key pair on the agreed curve; only the .pub.pem file is sentopenssl genpkey -algorithm X25519 -out my-x25519.pemopenssl pkey -in my-x25519.pem -pubout -out my-x25519.pub.pem openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out my-p256.pemopenssl pkey -in my-p256.pem -pubout -out my-p256.pub.pemfor curve in x25519 p256; do # ECDH: my private key + the peer's public key -> raw shared secret (32 bytes) openssl pkeyutl -derive -inkey alice-$curve.pem -peerkey bob-$curve.pub.pem -out $curve.secret secret=$(od -An -v -tx1 $curve.secret | tr -d ' \n') # HKDF-SHA256 turns the secret into a 32-byte key (no salt option = empty salt) key=$(openssl kdf -keylen 32 -kdfopt digest:SHA256 -kdfopt hexkey:$secret \ -kdfopt info:"AES-256-GCM key v1" -binary HKDF | od -An -v -tx1 | tr -d ' \n') echo "$curve shared secret: $secret" echo "$curve derived key: $key"doneHow it works
genpkey -algorithm X25519andgenpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256create the private keys;pkey -puboutextracts the public keys that are exchanged.pkeyutl -derive -inkey mine.pem -peerkey theirs.pub.pemwrites the raw 32-byte secret. Both sides run the same command with the roles swapped and get the same bytes.openssl kdf … HKDFtakes the secret ashexkey:, the purpose string asinfo:, and-keylen 32. Leaving outsaltgives the RFC 5869 default salt.-binarymakeskdfwrite raw bytes, whichodturns into the lowercase hex the other recipes print.
Gotchas
- Keys of different types fail with
Type of peer public key: X25519 does not match type of private key: EC, followed bypkeyutl: Error setting up peer key. - Pass the secret as
hexkey:(orkey:only for text).key:takes the characters themselves, sokey:4a5d…derives from the ASCII of the hex string and silently gives a different key. - Without
-binary,openssl kdfprints uppercase hex with colons (4E:CA:29:…). That is fine for reading, but strip the colons before comparing with other tools. - A malicious all-zero X25519 peer key makes
pkeyutl -derivefail withKey derivation failed(ossl_ecx_compute_key:failed during derivation) — check the exit code in scripts.