3
votes

I have a Diffie–Hellman security class like this:

public class AESSecurityCap {

    private PublicKey publicKey;
    KeyAgreement keyAgreement;
    byte[] sharedsecret;

    AESSecurityCap() {
        makeKeyExchangeParams();
    }

    private void makeKeyExchangeParams() {
        KeyPairGenerator kpg = null;
        try {
            kpg = KeyPairGenerator.getInstance("EC");
            kpg.initialize(128);
            KeyPair kp = kpg.generateKeyPair();
            publicKey = kp.getPublic();
            keyAgreement = KeyAgreement.getInstance("ECDH");
            keyAgreement.init(kp.getPrivate());

        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }
    }

    public void setReceiverPublicKey(PublicKey publickey) {
        try {
            keyAgreement.doPhase(publickey, false);  // <--- Error on this line
            sharedsecret = keyAgreement.generateSecret();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }
    }
} 

and implemented this class:

public class Node extends AESSecurityCap {
}

Sometimes I need to reinitialize DH keyAgreement:

public class TestMainClass {
    public static void main(String[] args) {
        Node server = new Node();
        Node client = new Node();

        server.setReceiverPublicKey(client.getPublicKey());
        client.setReceiverPublicKey(server.getPublicKey());

        // My problem is this line ,
        // Second time result exception
        server.setReceiverPublicKey(client.getPublicKey()); 
    }
}

but receive this exception:

Exception in thread "main" java.lang.IllegalStateException: Phase already executed
    at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.engineDoPhase(ECDHKeyAgreement.java:91)
    at java.base/javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:579)
    at ir.moke.AESSecurityCap.setReceiverPublicKey(AESSecurityCap.java:37)
    at ir.moke.TestMainClass.main(TestMainClass.java:13)

Is there any way to reinitialize ECDH KeyAgreement multiple time?

This is my test case:

  1. Client initialize DH and generate public key.
  2. Client sent public key to server.
  3. Server initialize DH with client key and generate own public key and generate shared secret key.
  4. Server send public key to client.
  5. Client generate shared secret key with server public key. In this step client and server has public keys and shared secret.

My problem is client disconnected() and KeyAgreement initialized by singleton object and don't reinitialized second time.

Sometimes I need to do this subject.

Please guide me to fix this problem.

1

1 Answers

1
votes

The IllegalStateException (Phase already executed) seems to be especially caused by the ECDH-implementation of the SunEC-provider. The exception doesn't occur if an (additional) init is executed immediately before the doPhase. However, this init-call shouldn't be necessary, since after the doPhase-call generateSecret is executed, which should reset the KeyAgreement-instance to the state after the init-call, at least according to the generateSecret-documentation:

This method resets this KeyAgreement object to the state that it was in after the most recent call to one of the init methods...

Possibly it's a bug in the SunEC-provider. If DH is used instead of ECDH (and the SunJCE-provider instead of the SunEC-provider) the behavior is as expected, i.e. repeated doPhase-calls are possible (without additional init-calls). The same applies to ECDH using the BouncyCastle-provider. Therefore, you could take the BouncyCastle-provider instead of the SunEC-provider to run ECDH with your code.

Note: The second parameter (lastPhase) in doPhase should be set to true, otherwise an IllegalStateException (Only two party agreement supported, lastPhase must be true) is generated (at least for ECDH).

EDIT:

The bug is already known and fixed in JDK 12, see JDK-8205476: KeyAgreement#generateSecret is not reset for ECDH based algorithmm.