# Verifying result of (EC-)Diffie-Hellman

I received a public key by JSON.

For the example, I have 4 keys: 2 public keys and 2 private keys.

public A : co2D0pNxZJIeQ4RZlCRJYBDzNXSLluETdztid0M+HGzN1uGJ4JWZsenjWgRrmkLh3yqHQqzOBMl/wHVH97A6+g==

private A : TXxii5Ka8LMvuc9arHu63qTmNKxGlgti+wpR3YhBGew=

public B : nUblC+OKdl94iBiWk0941wmYBiMt7C90CjOJPI2BPr8K7xGuC1XsR5DtwFCoM3Iew2BjBG+5SqrYwAPTJF7gdA==

private B : sm6V7+hChvkFSeLNoR+5tItiX8gH5tT47xBkFaV6SDU=


I can't make an ECDH exchange from a public key A and a private key B that I receive in JSON.

How can I verify : (public key A + private key B) == (public key B + private key A) ?

• The keys in the JSON file are Base64 encoded keys and usually can get imported with the key spec's. Then generate a shared secret using [public key A + private key B] and [public key B + private key A] and if the secrets are equal you have verified that have 2 working key pairs. Kindly edit your question and add some code as Stackoverflow is not a free code writing service, thanks. Aug 19 '21 at 16:00
• The public keys are 64 bytes after Base64 decoding, the private keys are 32 bytes, so they are raw EC keys. You have to import these keys, see e.g. here and here for the import as PublicKey and Privatekey, respectively. The agreement can then be determined with KeyAgreement.
– Topaco
Aug 19 '21 at 18:18
• Note that ECDH exchange does not use "addition", it uses point multiplication as underlying mathematical construct. Aug 20 '21 at 10:39
• Hint: decode the Base64, rule out that the content is ASN.1, tentatively guess it's big-endian raw bytes, conclude from sizes that the public keys probably have X and Y concatenated, use the high redundancy there to (hopefully) figure out a common curve (perhaps one of these), and check $a\,B=b\,A$ as asked. Alternatively, check if $a\,G=A$ and $b\,G=B$ (that is public and private keys match), which would prove $a\,B=b\,A$ without further computation. That reference may help (YMMV).
– fgrieu
Aug 20 '21 at 10:57

Usually the result of the (Elliptic Curve) Diffie-Hellman calculation is called a master secret. This master key is commonly used as input keying material (IKM) for a Key Derivation Function (KDF). Using the KDF multiple keys can be derived from the master secret. What you are looking for is called "key conformation".

There are various ways of confirming symmetric keys:

1. One of these keys can be used as input to a challenge response protocol. A challenge response protocol is a symmetric method to validate that an entity has access to the correct secret key.

2. Another way is to use the key as input to a MAC over a string known to both sides. That way the MAC shows that the entity has access to the correct key. For instance, TLS (1.3) uses a MAC over the entire handshake in the final "Finished" message of the handshake:

Finished: A MAC (Message Authentication Code) over the entire handshake. This message provides key confirmation, binds the endpoint's identity to the exchanged keys, and in PSK mode also authenticates the handshake. [Section 4.4.4]

3. You could also imagine that one party can sign the resulting secret and send the signature to the other party.

4. Finally, the confirmation that the resulting keys and thus secret are identical can also be performed implicitly when the first authenticated message is send from one party to the other party. This however is generally avoided, as it means that you cannot distinguish between data transmission and handshake errors; it can therefore play merry hell in protocol implementation.

Notes:

• If you confirm one key then the validity of the secret and any other derived key is also confirmed (barring implementation mistakes).
• It may be that the (static) public key of the DH can be trusted by other means, in which case additional verification of the resulting secret may not be required for the authenticated party; only the non-authenticated parties are required to send a challenge-response, MAC or signature value.

So this is how I solved my problem :

My keys in strings :

public A : co2D0pNxZJIeQ4RZlCRJYBDzNXSLluETdztid0M+HGzN1uGJ4JWZsenjWgRrmkLh3yqHQqzOBMl/wHVH97A6+g==

private A : TXxii5Ka8LMvuc9arHu63qTmNKxGlgti+wpR3YhBGew=

public B : nUblC+OKdl94iBiWk0941wmYBiMt7C90CjOJPI2BPr8K7xGuC1XsR5DtwFCoM3Iew2BjBG+5SqrYwAPTJF7gdA==

private B : sm6V7+hChvkFSeLNoR+5tItiX8gH5tT47xBkFaV6SDU=


Functions to get private key :

public static PrivateKey getPrivateKey(byte[] encodedPrivateKey) {
BigInteger s = new BigInteger(1,encodedPrivateKey);
ECNamedCurveParameterSpec ecCurve = ECNamedCurveTable.getParameterSpec("secp256r1");
ECParameterSpec ecParameterSpec = new ECNamedCurveSpec("secp256r1", ecCurve.getCurve(), ecCurve.getG(), ecCurve.getN(), ecCurve.getH(), ecCurve.getSeed());
ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(s, ecParameterSpec);
try {
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePrivate(privateKeySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
}

public static PublicKey rawToEncodedECPublicKey(String curveName, byte[] rawBytes) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException {
KeyFactory kf = KeyFactory.getInstance("EC");
byte[] x = Arrays.copyOfRange(rawBytes, 0, rawBytes.length/2);
byte[] y = Arrays.copyOfRange(rawBytes, rawBytes.length/2, rawBytes.length);
ECPoint w = new ECPoint(new BigInteger(1,x), new BigInteger(1,y));
return kf.generatePublic(new ECPublicKeySpec(w, ecParameterSpecForCurve(curveName)));
}

public static java.security.spec.ECParameterSpec ecParameterSpecForCurve(String curveName) throws NoSuchAlgorithmException, InvalidParameterSpecException {
AlgorithmParameters params = AlgorithmParameters.getInstance("EC");
params.init(new ECGenParameterSpec(curveName));
return params.getParameterSpec(ECParameterSpec.class);
}


We need to create 2 pairs of keys via keys public/private in string and check if both are equal :

byte [] cle_publique_a_decode = Base64.getDecoder().decode(cle_publique_a);
byte [] cle_privee_a_decode = Base64.getDecoder().decode(cle_privee_a);
byte [] cle_publique_b_decode = Base64.getDecoder().decode(cle_publique_b);
byte [] cle_privee_b_decode = Base64.getDecoder().decode(cle_privee_b);

try {
PublicKey PublicKeyA = rawToEncodedECPublicKey("secp256r1",cle_publique_a_decode);
PublicKey PublicKeyB = rawToEncodedECPublicKey("secp256r1",cle_publique_b_decode);

PrivateKey PrivateKeyA =  getPrivateKey(cle_privee_a_decode);
PrivateKey PrivateKeyB =  getPrivateKey(cle_privee_b_decode);

// Secret #1
// PrivateKeyA + PublicKeyB = generateSecret
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(PrivateKeyA);
keyAgreement.doPhase(PublicKeyB, true);
byte[] generateSecret = keyAgreement.generateSecret();
String base64_generateSecret = Base64.getEncoder().encodeToString(generateSecret);

// Secret #2
// PrivateKeyB + PublicKeyA = generateSecret2
KeyAgreement keyAgreement2 = KeyAgreement.getInstance("ECDH");
keyAgreement.init(PrivateKeyB);
keyAgreement.doPhase(PublicKeyA, true);
byte[] generateSecret2 = keyAgreement.generateSecret();
String base64_generateSecret2 = Base64.getEncoder().encodeToString(generateSecret);

// compare 2 secrets
// (public key A + private key B) == (public key B + private key A)
if(base64_generateSecret.equals(base64_generateSecret2)){
// Good : Secrets are same
// continue..
}
else{
// Not good : Secrets are differents
}
}
catch{
throw new IllegalArgumentException(e.getMessage(), e);
}


Keys are equal, I can begin to encrypt.

• The whole idea of DH is that you generate secrets at two locations. You cannot directly compare secrets by using equals as that would mean that you can copy the value from one location to another. If you can do that then you don't need DH. Aug 29 '21 at 9:28