我有一个Diffie-Hellman安全类,如下所示:

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


并实现了该类:

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?

这是我的测试用例:


客户端初始化DH并生成公共密钥。
客户端向服务器发送了公钥。
Cerver使用客户端密钥初始化DH,并生成自己的公共密钥并生成共享密钥。
服务器将公钥发送给客户端。
客户端使用服务器公共密钥生成共享密钥。
在此步骤中,客户端和服务器具有公共密钥和共享机密。


我的问题是客户端断开连接()和KeyAgreement由单例对象初始化,而不是第二次重新初始化。

有时我需要做这个主题。

请指导我解决此问题。

最佳答案

IllegalStateException (Phase already executed)似乎特别是由SunEC提供者的ECDH实现引起的。如果在init之前立即执行(附加)doPhase,则不会发生异常。但是,此init调用不是必需的,因为在执行doPhase -call generateSecret之后,至少应将KeyAgreement -instance重置为init -call之后的状态。根据generateSecret-文档:


  此方法将这个KeyAgreement对象重置为最近一次调用其中一个init方法之后的状态...


这可能是SunEC提供程序中的错误。如果使用DH而不是ECDH(并且使用SunJCE提供程序而不是SunEC提供程序),则行为符合预期,即可能重复doPhase-调用(无需其他init-调用)。使用BouncyCastle提供程序的ECDH同样适用。因此,可以使用BouncyCastle提供程序而不是SunEC提供程序来用代码运行ECDH。

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

编辑:

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

08-25 08:52