Issue
I have node js server for my android app and I want the communication to be secured using DH key exchange. I am able to generate the Prime p
and the Base g
in java same with nodejs(can be corrected if not right)
The Problem:
1. I want to use the secret p
and g
on both client side and server side since there will be encryption and decryption from both sides yet
crypto.createDiffieHellman()
nodejs function shown in documentation only takes the parameter of bitlength
so I have hard-coded these params after finding that other method signatures exist. From the documentation, I understand only public key should be shared. These p
and g
are generated initially on client side
2. In the sample code below, the key and secret generation works well in nodejs however when I test with public key from the client(generated using new String(Hex.encodeHex(kp.getPublic().getEncoded()))
), I get the Error: Supplied key is too large at DiffieHellman.dhComputeSecret
Fore more than a day, my thorough research reveals no examples/demo of DH key exchange between two different languages but one-language-examples with several comments/articles discussing the existing wiki docs and less or no code. I think this is a new question on cross language client-server with DH keys.
Nodejs:
var crypto = require("crypto");
var assert = require("assert");
// the prime is shared by everyone
var p ="7287927445664946359687239486223244248530331441696747442753348226106279800740207968417650493105155177035805265358863967196769895080354517146585830093038907";
var g="3519494160132765249824212078020429853238178237852596056897517714427148886750491954575952859563331424777669938328249242087811536189850912851560984760609308";
let server = crypto.createDiffieHellman(Buffer.from(p,).toString("hex"),"hex",Buffer.from(g,).toString("hex"),"hex");
let prime = server.getPrime();
console.log("Generate Alice's keys...");
console.log(Buffer.from(prime).toString());
let alice = crypto.createDiffieHellman(prime);
let alicePublicKey = alice.generateKeys();
console.log("Generate Bob's keys...");
let bob = crypto.createDiffieHellman(prime);
//let bobPublicKey = bob.generateKeys();
//THIS IS PUBLIC KEY FROM JAVA IN HEX FORMAT
let bobPublicKey = Buffer.from("3081df30819706092a864886f70d0103013081890241008b26a5b60fd75471c366a0e8f67abe84d6f4c16b0dc97a602937420af3a658b34bb484df2d85281417a1bde4c0c51a7f97e8ac3a70fb34f092c2b1ebba01253b02404332edd9db0820ef954487cc1b327c638e2876b88a0cabc498811b42f7528af7fe58286a521f2190e9cc8785feaa5a8f019624bf88587a8b5b79854a11bcea1c02020180034300024012e116bc4fbd90542e03c1a5a130f923e579b65a50b8ed02e61f6369375f3a17ef270b3d05c52085ffe6e185ec7b19ea3cba7fe40d87e62254dc15f0e6db63b5","hex");
console.log("Exchange and generate the secret...");
let aliceBobSecret = alice.computeSecret(bobPublicKey);
let bobAliceSecret = bob.computeSecret(alicePublicKey);
// let davidAliceSecret = david.computeSecret(alicePublicKey, "latin1");
// let aliceDavidSecret = alice.computeSecret(davidPublicKey, "latin1");
console.log("alicePublicKey", alicePublicKey);
console.log("bobPublicKey", bobPublicKey);
assert.notEqual(alicePublicKey, bobPublicKey);
console.log("alicePublicKey and bobPublicKey NOT equal");
// console.log("aliceBobSecret", aliceBobSecret.toString("latin1"));
// console.log("bobAliceSecret", bobAliceSecret.toString("latin1"));
assert.equal(aliceBobSecret.toString("hex"), bobAliceSecret.toString("hex"));
console.log("aliceBobSecret and bobAliceSecret equal");
Java:
//THIS COMMENTED CODE IS USED TO INITIALLY GENERATE p AND g
/*AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH");
paramGen.init(512); // number of bits
AlgorithmParameters params = paramGen.generateParameters();
DHParameterSpec dhSpec = (DHParameterSpec) params.getParameterSpec(DHParameterSpec.class);
BigInteger p512 = dhSpec.getP();
BigInteger g512 = dhSpec.getG();*/
BigInteger p512 = new BigInteger(Hex.decodeHex("008b26a5b60fd75471c366a0e8f67abe84d6f4c16b0dc97a602937420af3a658b34bb484df2d85281417a1bde4c0c51a7f97e8ac3a70fb34f092c2b1ebba01253b"));
BigInteger g512 = new BigInteger(Hex.decodeHex("4332edd9db0820ef954487cc1b327c638e2876b88a0cabc498811b42f7528af7fe58286a521f2190e9cc8785feaa5a8f019624bf88587a8b5b79854a11bcea1c"));
//A
KeyPairGenerator akpg = KeyPairGenerator.getInstance("DiffieHellman");
DHParameterSpec param = new DHParameterSpec(p512, g512);
String a = p512 + "";
String b = g512 + "";
System.out.println("Prime: " + new String(Hex.encodeHex(p512.toByteArray())));
System.out.println("PrimeH: " + p512);
System.out.println("Base: " + new String(Hex.encodeHex(g512.toByteArray())));
System.out.println("BaseH: " + g512);
akpg.initialize(param);
KeyPair kp = akpg.generateKeyPair();
//B
KeyPairGenerator bkpg = KeyPairGenerator.getInstance("DiffieHellman");
DHParameterSpec param2 = new DHParameterSpec(p512, g512);
System.out.println("Prime: " + p512);
System.out.println("Base: " + g512);
bkpg.initialize(param2);
KeyPair kp2 = bkpg.generateKeyPair();
KeyAgreement aKeyAgree = KeyAgreement.getInstance("DiffieHellman");
KeyAgreement bKeyAgree = KeyAgreement.getInstance("DiffieHellman");
aKeyAgree.init(kp.getPrivate());
bKeyAgree.init(kp2.getPrivate());
System.out.println("2Key: " +new String(Hex.encodeHex(kp.getPublic().getEncoded())));
aKeyAgree.doPhase(kp2.getPublic(), true);
bKeyAgree.doPhase(kp.getPublic(), true);
//System.out.println("Alice Secret Key: " + aKeyAgree.generateSecret());
//System.out.println("Bob's Secret Key: " + bKeyAgree.generateSecret());
MessageDigest hash = MessageDigest.getInstance("SHA-256");
/*byte[] ASharedSecret = hash.digest(aKeyAgree.generateSecret());
byte[] BSharedSecret = hash.digest(bKeyAgree.generateSecret());*/
byte[] ASharedSecret = aKeyAgree.generateSecret();
byte[] BSharedSecret = bKeyAgree.generateSecret();
System.out.println("Alice's Shared Secret: " + Arrays.toString(ASharedSecret));
System.out.println("Bob's Shared Secret: " + Arrays.toString(BSharedSecret));
Solution
Here is a working example of DH key exchange within NodeJS and Java.
Node:
// Step 1. Generate Alice DH key in NodeJS side
let crypto = require('crypto');
let dh = crypto.getDiffieHellman('modp14');
dh.generateKeys();
let alicePublicKey = dh.getPublicKey().toString('hex');
console.log(alicePublicKey); // 308a5cff.....e6ded9d5 ----> send to Bob
// Step 5. Check shared secret
let bobPublicKey = "00a8e459...61e210d9"; // <---- receive from Bob
let shared = dh.computeSecret(Buffer.from(bobPublicKey, "hex"), null, 'hex');
console.log(shared); // dd83430d...22156ebd - must be the same as in the Java side
Java:
void testDH() {
// Step 2. Configure DH params on Java side
DHParameterSpec modp14 = modp14();
// Step 3. Encode public key from Alice
byte[] alicePubKeyEnc = hexStringToByteArray("308a5cff.....e6ded9d5");
BigInteger alicePublic = new BigInteger(alicePubKeyEnc);
KeyFactory bobKeyFactory = KeyFactory.getInstance("DH");
DHPublicKeySpec bobPubKeySpecs = new DHPublicKeySpec(
alicePublic, // <---- use Alice's public
modp14.getP(),
modp14.getG());
DHPublicKey alicePubKey = (DHPublicKey)bobKeyFactory.generatePublic(bobPubKeySpecs);
// Step 4. Generate Bob DH key in Java side
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(modp14);
KeyPair bobKpair = bobKpairGen.generateKeyPair();
System.out.println("Bob public key: "
+ bytesToHex(((DHPublicKey)bobKpair.getPublic()).getY().toByteArray()));
// 00a8e459...61e210d9
// Step 6. Generates shared secret
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKpair.getPrivate());
bobKeyAgree.doPhase(alicePubKey, true);
byte[] bobSharedSecret = new byte[alicePubKeyEnc.length];
bobKeyAgree.generateSecret(bobSharedSecret, 0);
System.out.println("Bob's shared secret: "
+ bytesToHex(bobSharedSecret)); // dd83430d...22156ebd
}
public static DHParameterSpec modp14() {
final BigInteger p =
new BigInteger(
"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1c" +
"d129024e088a67cc74020bbea63b139b22514a08798e3404" +
"ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c2" +
"45e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7" +
"edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b" +
"3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf" +
"5f83655d23dca3ad961c62f356208552bb9ed52907709696" +
"6d670c354e4abc9804f1746c08ca18217c32905e462e36ce" +
"3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52" +
"c9de2bcbf6955817183995497cea956ae515d2261898fa05" +
"1015728e5a8aacaa68ffffffffffffffff",
16);
final BigInteger g = new BigInteger("2");
return new DHParameterSpec(p, g);
}
- Tested using NodeJS v17.0.1 and OpenJDK 14.0.1.
- See this SO question about using
modp14
Answered By - Alexander