CryptoSys Home > PKI > Compute ECDH shared secret using P-256 and X25519

Compute ECDH shared secret using P-256 and X25519 using CryptoSys PKI Pro


Compute ECDH shared secret using NIST P-256

C# code

Console.WriteLine("\nECDH SHARED SECRET USING P-256:");
// Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2)
// Test Information for "testecccdh" ecccdhtestvectors.zip
// [P-256]
Ecc.CurveName curve = Ecc.CurveName.P_256;
// Our private key is dUIT:
string ourPrivateKeyHex = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534";
// Their public key as hex is "04" || QCAVSx || QCAVSy
string theirPublicKeyHex = "04" + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
	+ "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac";
// Correct expected result
string zzOK = "46FC62106420FF012E54A434FBDD2D25CCC5852060561E68040DD7778997BD7B";
string ourPrivateKey = Ecc.ReadKeyByCurve(ourPrivateKeyHex, curve);
Console.WriteLine("Curve = {0}", Ecc.QueryKey(ourPrivateKey, "curveName"));
Console.WriteLine("Our private key has {0} bits", Ecc.QueryKey(ourPrivateKey, "keyBits"));
string theirPublicKey = Ecc.ReadKeyByCurve(theirPublicKeyHex, curve);
Console.WriteLine("Their public key has {0} bits", Ecc.QueryKey(theirPublicKey, "keyBits"));

// Compute shared secret
byte[] zz = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey);
Console.WriteLine("ZZ={0}", Cnv.ToHex(zz));
Debug.Assert(Cnv.ToHex(zz) == zzOK, "Ecc.DHSharedSecret failed");

VBA/VB6 code

For similar code using VBA/VB6 see the example in ECC_DHSharedSecret and the (shorter) example below. This shorter example uses the new VBA/VB6 wrapper functions introduced v20.0.1.

Debug.Print "Testing ECC_DHSharedSecret ..."
Dim strOurPriKeyHex As String
Dim strTheirPubKeyHex As String
Dim strOurPrivateKey As String
Dim strTheirPublicKey As String
Dim strCurveName As String
Dim strCorrectHex As String
Dim lpZZ() As Byte

' Ref: CAVS 14.1 ECC CDH Primitive (SP800 - 56A Section 5.7.1.2)
' Test Information for "testecccdh" ecccdhtestvectors.zip
'[P-256]
strCurveName = "P-256"
' Our private key is dUIT:
strOurPriKeyHex = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534"
' Their public key as hex is "04" || QCAVSx || QCAVSy
strTheirPubKeyHex = "04" & "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" _
	+ "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac"
' Correct result is ZIUT
strCorrectHex = "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"

Debug.Print "Test for curve " & strCurveName & " ..."
' Read in keys to internal key strings
' Our private key...
strOurPrivateKey = eccReadKeyByCurve(strOurPriKeyHex, strCurveName, 0)
Debug.Assert Len(strOurPrivateKey) > 0
' Check the key we read in
Debug.Print "Our private key has " & ECC_QueryKey("", 0, strOurPrivateKey, "keyBits", 0) & " bits"

' Their public key...
strTheirPublicKey = eccReadKeyByCurve(strTheirPubKeyHex, strCurveName, 0)
Debug.Assert Len(strTheirPublicKey) > 0
' Check the key we read in
Debug.Print "Their public key has " & ECC_QueryKey("", 0, strTheirPublicKey, "keyBits", 0) & " bits"

' Compute shared secret
lpZZ = eccDHSharedSecret(strOurPrivateKey, strTheirPublicKey, 0)
Debug.Assert cnvBytesLen(lpZZ) > 0

Debug.Print "ZZ=" & cnvHexStrFromBytes(lpZZ)
' Check the result
Debug.Print "OK=" & strCorrectHex
Debug.Assert UCase(cnvHexStrFromBytes(lpZZ)) = UCase(strCorrectHex)

Compute ECDH shared secret using X25519

C# code

Console.WriteLine("\nECDH SHARED SECRET USING X25519:");

// Ref: RFC7748 Section 6.1
// https://tools.ietf.org/html/rfc7748#section-6.1
/*
Test vector:
Alice's private key, a:
	77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
Alice's public key, X25519(a, 9):
	8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a
Bob's private key, b:
	5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
Bob's public key, X25519(b, 9):
	de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f
Their shared secret, K:
	4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742
*/
Ecc.CurveName curve = Ecc.CurveName.X25519;

string alicePrivateKeyHex = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
string alicePublicKeyHex = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
string bobPrivateKeyHex = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
string bobPublicKeyHex = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
// Correct expected result
string zzOK = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742";

// 1. Alice's private + Bob's public
// NB we *must* specify whether X25519 keys are public or private
string ourPrivateKey = Ecc.ReadKeyByCurve(alicePrivateKeyHex, curve, Ecc.KeyType.PrivateKey);
Console.WriteLine("Curve = {0}", Ecc.QueryKey(ourPrivateKey, "curveName"));
Console.WriteLine("Our private key has {0} bits", Ecc.QueryKey(ourPrivateKey, "keyBits"));
string theirPublicKey = Ecc.ReadKeyByCurve(bobPublicKeyHex, curve, Ecc.KeyType.PublicKey);
Console.WriteLine("Their public key has {0} bits", Ecc.QueryKey(theirPublicKey, "keyBits"));
// Compute shared secret
byte[] zz1 = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey);
Console.WriteLine("ZZ={0}", Cnv.ToHex(zz1));
Debug.Assert(Cnv.ToHex(zz1).ToLower() == zzOK, "Ecc.DHSharedSecret failed");

// 2. Bob's private + Alice's public
ourPrivateKey = Ecc.ReadKeyByCurve(bobPrivateKeyHex, curve, Ecc.KeyType.PrivateKey);
theirPublicKey = Ecc.ReadKeyByCurve(alicePublicKeyHex, curve, Ecc.KeyType.PublicKey);
// Compute shared secret
byte[] zz2 = Ecc.DHSharedSecret(ourPrivateKey, theirPublicKey);
Console.WriteLine("ZZ={0}", Cnv.ToHex(zz2));
Debug.Assert(Cnv.ToHex(zz2).ToLower() == zzOK, "Ecc.DHSharedSecret failed");

VBA/VB6 code

' Ref: RFC7748 Section 6.1
' https://tools.ietf.org/html/rfc7748#section-6.1
'
'    Test vector:
'    Alice's private key, a:
'        77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a
'    Alice's public key, X25519(a, 9):
'        8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a
'    Bob's private key, b:
'        5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb
'    Bob's public key, X25519(b, 9):
'        de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f
'    Their shared secret, K:
'        4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742

' NB uses functions in basCrPKIWrappers.bas, available in v20.0.1
Dim curveName As String
Dim alicePrivateKeyHex As String
Dim alicePublicKeyHex As String
Dim bobPrivateKeyHex As String
Dim bobPublicKeyHex As String
Dim zzOK As String
Dim ourPrivateKey As String
Dim theirPublicKey As String
Dim zz1() As Byte
Dim zz2() As Byte

curveName = "X25519"
alicePrivateKeyHex = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"
alicePublicKeyHex = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
bobPrivateKeyHex = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"
bobPublicKeyHex = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"
' Correct expected result
zzOK = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"

' 1. Alice's private + Bob's public
' NB we *must* specify whether X25519 keys are public or private
ourPrivateKey = eccReadKeyByCurve(alicePrivateKeyHex, curveName, PKI_ECC_PRIVATE_KEY)
Debug.Assert Len(ourPrivateKey) > 0
Debug.Print "Our private key has " & ECC_QueryKey("", 0, ourPrivateKey, "keyBits", 0) & " bits"
theirPublicKey = eccReadKeyByCurve(bobPublicKeyHex, curveName, PKI_ECC_PUBLIC_KEY)
Debug.Assert Len(theirPublicKey) > 0
Debug.Print "Their public key has " & ECC_QueryKey("", 0, theirPublicKey, "keyBits", 0) & " bits"
' Compute shared secret
zz1 = eccDHSharedSecret(ourPrivateKey, theirPublicKey, 0)
Debug.Print "ZZ=" & cnvHexStrFromBytes(zz1)
Debug.Assert StrComp(cnvHexStrFromBytes(zz1), zzOK, vbTextCompare) = 0

Contact us

To contact us or comment on this page, please send us a message.

[Go to top]

This page last updated 10 September 2025