Java Diffie hellman 初始化 ECDHKeyAgreement

Java Diffie hellman initialize ECDHKeyAgreement

我有一个像这样的 Diffie–Hellman 安全 class:

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();
        }
    }
} 

并实现了这个 class:

public class Node extends AESSecurityCap {
}

有时我需要重新初始化 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()); 
    }
}

但收到此异常:

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)

有没有办法多次重新初始化ECDH KeyAgreement?

这是我的测试用例:

  1. 客户端初始化 DH 并生成 public 密钥。
  2. 客户端向服务器发送了 public 密钥。
  3. 服务器使用客户端密钥初始化 DH 并生成自己的 public 密钥并生成共享密钥。
  4. 服务器向客户端发送public密钥。
  5. 客户端使用服务器 public 密钥生成共享密钥。 在此步骤中,客户端和服务器有 public 个密钥和共享密钥。

我的问题是客户端 disconnected() 和 KeyAgreement 由单例对象初始化并且没有第二次重新初始化。

有时候我需要做这门课。

请指导我解决这个问题。

IllegalStateException (Phase already executed) 似乎是由 SunEC 提供商的 ECDH 实现引起的。如果执行(附加的)init is executed immediately before the doPhase. However, this init-call shouldn't be necessary, since after the doPhase-call generateSecret,则不会发生异常,这应该将 KeyAgreement-实例重置为 init-调用之后的状态,至少根据 generateSecret-文档:

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

可能是 SunEC 提供程序中的错误。如果使用 DH 而不是 ECDH(并且使用 SunJCE 提供者而不是 SunEC 提供者),则行为符合预期,即重复 doPhase-calls 是可能的(无需额外的 init-calls)。这同样适用于使用 BouncyCastle-provider 的 ECDH。因此,您可以使用您的代码将 BouncyCastle-provider 而不是 SunEC-provider 带到 运行 ECDH。

注意doPhase中的第二个参数(lastPhase)应该设置为true,否则会生成一个IllegalStateException (Only two party agreement supported, lastPhase must be true)(at至少对于 ECDH)。

编辑:

该错误已知并已在 JDK 12 中修复,请参阅 JDK-8205476: KeyAgreement#generateSecret is not reset for ECDH based algorithmm