Bogdan Makarenko
Bogdan Makarenko

Reputation: 13

Diffie-Hellman algorithm between C# and Java

I have console applications in c# and Java. Both of them generate public and private keys for Eliptic curved Diffie-Hellman algorithm. Public keys are encrypted in base64 and then are printed in console. Then I paste public key from c# into java program and vice verca. Unforutunately in the end derived keys which have to be the same are different. Seems like the configurations of algorithms are the same and there are no exceptions.
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)));
    }

Upvotes: 1

Views: 1673

Answers (1)

Ruslan Gilmutdinov
Ruslan Gilmutdinov

Reputation: 1417

Your Java code does some extra work to obtain derived key. Just remove following lines and you will get the same key as in С# code:

// 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));

Here is the whole Java code just for completeness:

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

Upvotes: 2

Related Questions