Diffie-Hellman key exchange in Java

2019-03-19 04:34发布

问题:

I am working on a personal project in Java which involves sending sensitive data over an insecure channel. I need to know how to implement Diffie Hellman Key Exchange (DHKE) in java using its libraries. I know all the cryptographic theory about it so no need to go into details, I just need a very basic implementation so I cand have 2 programs share a secret key. I got the example from java2s.com, but it is not complete:

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;

import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.DHPublicKeySpec;

public class Main {
  public final static int pValue = 47;

  public final static int gValue = 71;

  public final static int XaValue = 9;

  public final static int XbValue = 14;

  public static void main(String[] args) throws Exception {
    BigInteger p = new BigInteger(Integer.toString(pValue));
    BigInteger g = new BigInteger(Integer.toString(gValue));
    BigInteger Xa = new BigInteger(Integer.toString(XaValue));
    BigInteger Xb = new BigInteger(Integer.toString(XbValue));

    int bitLength = 512; // 512 bits
    SecureRandom rnd = new SecureRandom();
    p = BigInteger.probablePrime(bitLength, rnd);
    g = BigInteger.probablePrime(bitLength, rnd);

    createSpecificKey(p, g);
  }

  public static void createSpecificKey(BigInteger p, BigInteger g) throws Exception {
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("DiffieHellman");

    DHParameterSpec param = new DHParameterSpec(p, g);
    kpg.initialize(param);
    KeyPair kp = kpg.generateKeyPair();

    KeyFactory kfactory = KeyFactory.getInstance("DiffieHellman");

    DHPublicKeySpec kspec = (DHPublicKeySpec) kfactory.getKeySpec(kp.getPublic(),
        DHPublicKeySpec.class);
  }
}

How do I go on from this? Could anyone help me complete the remaining code?

回答1:

What about the official Oracle Docs? They show a DH key exchange in code there.



回答2:

Here's a work example:

static void main() {

    DH dh = new DH();
    byte[] myPublicKey = dh.generatePublicKey();

    /* Send myPublicKey to other party, and get hisPublicKey in return */

    byte[] sharedKey = dh.computeSharedKey(hisPublicKey)
    /* sharedKey is now 'shared' between both parties */

}

public class DH {

    private static final String TAG = "DH";

    private KeyPair keyPair;
    private KeyAgreement keyAgree;

    public byte[] generatePublicKey() {
        DHParameterSpec dhParamSpec;

        try {
            dhParamSpec = new DHParameterSpec(P, G);
            Log.i(TAG, "P = " + P.toString(16));
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DiffieHellman");
            keyPairGen.initialize(dhParamSpec);
            keyPair = keyPairGen.generateKeyPair();
            Log.i(TAG, "Y = " + ((DHPublicKey) keyPair.getPublic()).getY().toString(16));
            keyAgree = KeyAgreement.getInstance("DiffieHellman");
            keyAgree.init(keyPair.getPrivate());

            BigInteger pubKeyBI = ((DHPublicKey) keyPair.getPublic()).getY();
            byte[] pubKeyBytes = pubKeyBI.toByteArray();
            Log.i(TAG, String.format(TAG, "Y [%d] = %s", pubKeyBytes.length, Utils.toHexString(pubKeyBytes)));
            return pubKeyBytes;
        } catch (Exception e) {
            Log.e(TAG, "generatePubKey(): " + e.getMessage());
            return null;
        }
    }

    public byte[] computeSharedKey(byte[] pubKeyBytes) {
        if (keyAgree == null) {
            Log.e(TAG, "computeSharedKey(): keyAgree IS NULL!!");
            return null;
        }

        try {
            KeyFactory keyFactory = KeyFactory.getInstance("DiffieHellman");
            BigInteger pubKeyBI = new BigInteger(1, pubKeyBytes);
            Log.i(TAG, "Y = " + pubKeyBI.toString(16));
            PublicKey pubKey = keyFactory.generatePublic(new DHPublicKeySpec(pubKeyBI, P, G));
            keyAgree.doPhase(pubKey, true);
            byte[] sharedKeyBytes = keyAgree.generateSecret();
            Log.i(TAG, String.format("SHARED KEY[%d] = %s", sharedKeyBytes.length, Utils.toHexString(sharedKeyBytes)));
            return sharedKeyBytes;
        } catch (Exception e) {
            Log.e(TAG, "computeSharedKey(): " + e.getMessage());
            return null;
        }
    }

    private static final byte P_BYTES[] = {
            (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,
            ...
            (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7
    };
    private static final BigInteger P = new BigInteger(1, P_BYTES);

    private static final BigInteger G = BigInteger.valueOf(2);
}


回答3:

Following code uses Elliptic Curve Diffie-Hellman to generate and share 128bit key and AES for encryption.

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.*;

public class AESSecurityCap {

private PublicKey publickey;
KeyAgreement keyAgreement;
byte[] sharedsecret;

String ALGO = "AES";

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, true);
        sharedsecret = keyAgreement.generateSecret();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
}

public String encrypt(String msg) {
    try {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.ENCRYPT_MODE, key);
        byte[] encVal = c.doFinal(msg.getBytes());
        return new BASE64Encoder().encode(encVal);
    } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return msg;
}

public String decrypt(String encryptedData) {
    try {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGO);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
        byte[] decValue = c.doFinal(decordedValue);
        return new String(decValue);
    } catch (BadPaddingException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | IOException e) {
        e.printStackTrace();
    }
    return encryptedData;
}

public PublicKey getPublickey() {
    return publickey;
}

protected Key generateKey() {
    return new SecretKeySpec(sharedsecret, ALGO);
}
}

Extend your own class to add AES encrypting feature

public class Node extends AESSecurityCap {

//your class

}

Finally, way to use the encryption

public class Main {

public static void main(String[] args) throws IOException {
    Node server = new Node();
    Node client = new Node();

    server.setReceiverPublicKey(client.getPublickey());

    client.setReceiverPublicKey(server.getPublickey());

    String data = "hello";

    String enc = server.encrypt(data);

    System.out.println("hello is coverted to "+enc);

    System.out.println(enc+" is converted to "+client.decrypt(enc));

}
}

output:

hello is coverted to OugbNvUuylvAr9mKv//nLA==
OugbNvUuylvAr9mKv//nLA== is converted to hello

Process finished with exit code 0