PaulYu
PaulYu

Reputation: 11

How to implement DH keyagreement referring these Java codes?

I have written X25519 DH keyagreement with Java, but I have to use nodejs to reimplement this, so that I can make keyagreement between js client and java backend.

I used node crypto module, but the length of shared key is not the same regarding implemented by Java.

Here is my Java code, and could anybody help me show the nodejs codes. Thanks.

package com.demo;

import java.util.Base64;

import javax.crypto.KeyAgreement;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.X509EncodedKeySpec;

public class Main {

    public static void main(String[] args) {
    // write your code here
        System.out.println("Hello world");

        String peerPub = "MCowBQYDK2VuAyEAfMePklV88QMhq8qlVxLI6RK1pV4cFUrMwJgPmrXLyVU=";
        try {
            buildSecret(peerPub);
        }
        catch (Exception e) {

        }

    }

    public static void buildSecret(String peerPub) throws Exception {
        KeyPairGenerator kpgen = KeyPairGenerator.getInstance("XDH");
        kpgen.initialize(new ECGenParameterSpec("X25519"));
        KeyPair myKP = kpgen.generateKeyPair();

        byte[] pp = Base64.getDecoder().decode(peerPub);
        PublicKey peerKey = bytesToPublicKey(pp);

        KeyAgreement ka = KeyAgreement.getInstance("XDH");
        ka.init(myKP.getPrivate());
        ka.doPhase(peerKey, true);

//        System.out.println( myKP.getPublic().getEncoded().length );
        String publicKey = Base64.getEncoder().encodeToString(myKP.getPublic().getEncoded());

//        System.out.println( ka.generateSecret().length );
        String sharedKey = Base64.getEncoder().encodeToString(ka.generateSecret());

        System.out.println(publicKey);
        System.out.println(sharedKey);
    }

    private static PublicKey bytesToPublicKey(byte[] data) throws Exception {
        KeyFactory kf = KeyFactory.getInstance("X25519");
        return kf.generatePublic(new X509EncodedKeySpec(data));
    }
}

And the nodejs code is following(not working):

const crypto = require('crypto');

const ecdhKeyagreement = () => {
  const CURVE = 'x25519';
  let m_privateKey;
  let m_publicKey;
  let m_sharedKey;

  const generatePublicAndPrivateKeys = () => {

    const {publicKey, privateKey} = crypto.generateKeyPairSync('x25519', {
      modulusLength: 4096,
      publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
      },
      privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem'
      }
    })

    m_privateKey = privateKey
    m_publicKey = publicKey

  }

  const computeSharedKey = (peerPub) => {
    // console.log(m_publicKey)
    // console.log(m_privateKey)

    const bob = crypto.createDiffieHellman(512)
    bob.setPrivateKey(m_privateKey)
    m_sharedKey = bob.computeSecret(peerPub).toString('base64')

    console.log(m_sharedKey)

  };


  return {
    generatePublicAndPrivateKeys,
    computeSharedKey,
  };
};

const my_obj = ecdhKeyagreement();
my_obj.generatePublicAndPrivateKeys()

const peerPub = "MCowBQYDK2VuAyEAME2NXThH2T+PMTV2R2YGo5hYiVFhu7nbQGY0R89aYFE="

my_obj.computeSharedKey(peerPub)

Upvotes: 0

Views: 450

Answers (1)

dave_thompson_085
dave_thompson_085

Reputation: 38781

You don't show your nodejs code where the (presumed) problem is, which is the accepted practice on StackOverflow, but since you came halfway:

with your Java code, modified (only) to use the public half of one of my test (static) keypairs, and run on j16 because 11-15 apparently produce an algid with parameters which violates RFC8410 and is rejected by OpenSSL and thus nodejs crypto which uses OpenSSL, and the following straightforward js code using the corresponding private key (run on v14.15.5), I get exactly the same agreement result as Java:

const crypto = require('crypto'), fs = require('fs')
const peerb64 = "MCowBQYDK2VuAyEAZWJZEjPzc6E4UUSyOcMmxj2cRqqmDhE4/VfyPyfe7j4="
console.log("sRbfKaWmO9u2eKWSfY25i8Z2YFNNiLYeVcoh6DOI2ik=") // expected agreement
const myprv = crypto.createPrivateKey(fs.readFileSync('n:certx/x2.pem'))
const peerpub = crypto.createPublicKey({key:Buffer.from(peerb64,'base64'),format:'der',type:'spki'})
console.log( crypto.diffieHellman({privateKey:myprv,publicKey:peerpub}) .toString('base64') )

Upvotes: 1

Related Questions