C#和Java之间的Diffie-Hellman算法

问题描述

我有 C# 和 Java 的控制台应用程序。它们都为 Eliptic 曲线 Diffie-Hellman 算法生成公钥和私钥。公钥在 base64 中加密,然后在控制台中打印。然后我将公钥从 c# 粘贴到 java 程序中,反之亦然。 不幸的是,最终必须相同的派生密钥不同。 似乎算法的配置是相同的,没有例外。
C#:

static void Main(string[] args)
        {
            ECDiffieHellmanCng eCDiffie = new ECDiffieHellmanCng(256);
            eCDiffie.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
            eCDiffie.HashAlgorithm = Cngalgorithm.Sha256;



            byte[] myPublicKey = eCDiffie.ExportSubjectPublicKeyInfo();   //export in x509 format
            String myPublicKeyBase64 = Convert.ToBase64String(myPublicKey);
            Console.WriteLine(myPublicKeyBase64);



            string otherKey = Console.ReadLine();  // here paste public key in console from Java
            byte[] otherKeyFromBase64 = Convert.FromBase64String(otherKey);
            ECDiffieHellmanCng eCDiffie2 = new ECDiffieHellmanCng(256);
            eCDiffie2.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
            eCDiffie2.HashAlgorithm = Cngalgorithm.Sha256;
            int some = 0;
            eCDiffie2.ImportSubjectPublicKeyInfo(otherKeyFromBase64,out some);


            byte[] otherKeyDecoded = eCDiffie2.PublicKey.ToByteArray();
            CngKey k = CngKey.Import(otherKeyDecoded,CngKeyBlobFormat.EccpublicBlob);
            byte[] derivedKey = eCDiffie.DeriveKeyMaterial(k);
            String derivedKeyBase64 = Convert.ToBase64String(derivedKey);



            Console.WriteLine("Derived key: ");
            Console.WriteLine(derivedKeyBase64);
        }

Java:

public static void main(String[] args) throws NoSuchAlgorithmException,InvalidKeyException,InvalidKeySpecException {
        // Generate ephemeral ECDH keypair
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
        kpg.initialize(256);
        KeyPair kp = kpg.generateKeyPair();
        byte[] ourPk = kp.getPublic().getEncoded();  //public key in x509 format

        // display our public key
        byte[] ourPublicKeyBase64 = Base64.getEncoder().encode(ourPk);
        System.out.println(String.format("Public Key: %s",new String(ourPublicKeyBase64)));

        // Read other's public key:
        Scanner in = new Scanner(system.in);
        String oth = in.nextLine();   // here paste in console public key C#
        byte[] otherPk = Base64.getDecoder().decode(oth);


        KeyFactory kf = KeyFactory.getInstance("EC");
        X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(otherPk);
        PublicKey otherPublicKey = kf.generatePublic(pkSpec);

        // Perform key agreement
        KeyAgreement ka = KeyAgreement.getInstance("ECDH");
        ka.init(kp.getPrivate());
        ka.doPhase(otherPublicKey,true);
        // Read shared secret
        byte[] sharedSecret = ka.generateSecret();



        // Derive a key from the shared secret and both public keys
        MessageDigest hash = MessageDigest.getInstance("SHA-256");
        hash.update(sharedSecret);
        // Simple deterministic ordering
        List<ByteBuffer> keys = Arrays.asList(ByteBuffer.wrap(ourPk),ByteBuffer.wrap(otherPk));
        Collections.sort(keys);
        hash.update(keys.get(0));
        hash.update(keys.get(1));

        byte[] derivedKey = hash.digest();
        byte[] derivedKeyBase64 = Base64.getEncoder().encode(derivedKey);
        System.out.println(String.format("Derived key: %s",new String(derivedKeyBase64)));
    }

解决方法

您的 Java 代码做了一些额外的工作来获取派生密钥。只需删除以下几行,您将获得与 С# 代码中相同的密钥:

// Simple deterministic ordering
List<ByteBuffer> keys = Arrays.asList(ByteBuffer.wrap(ourPk),ByteBuffer.wrap(otherPk));
Collections.sort(keys);
hash.update(keys.get(0));
hash.update(keys.get(1));

以下是完整的 Java 代码:

public static void main(String[] args) throws Exception {
    // Generate ephemeral ECDH keypair
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
    kpg.initialize(256);
    KeyPair kp = kpg.generateKeyPair();
    byte[] ourPk = kp.getPublic().getEncoded();  //public key in x509 format

    // Display our public key
    byte[] ourPublicKeyBase64 = Base64.getEncoder().encode(ourPk);
    System.out.println(String.format("Public Key: %s",new String(ourPublicKeyBase64)));

    // Read other's public key:
    Scanner in = new Scanner(System.in);
    String oth = in.nextLine();   // here paste in console public key C#
    byte[] otherPk = Base64.getDecoder().decode(oth);

    KeyFactory kf = KeyFactory.getInstance("EC");
    X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(otherPk);
    PublicKey otherPublicKey = kf.generatePublic(pkSpec);

    // Perform key agreement
    KeyAgreement ka = KeyAgreement.getInstance("ECDH");
    ka.init(kp.getPrivate());
    ka.doPhase(otherPublicKey,true);
    
    // Read shared secret
    byte[] sharedSecret = ka.generateSecret();

    // Derive a key from the shared secret
    MessageDigest hash = MessageDigest.getInstance("SHA-256");
    hash.update(sharedSecret);        

    byte[] derivedKey = hash.digest();
    byte[] derivedKeyBase64 = Base64.getEncoder().encode(derivedKey);
    System.out.println(String.format("Derived key: %s",new String(derivedKeyBase64)));
}