Skip to content

Instantly share code, notes, and snippets.

@joubin
Created March 9, 2016 03:07
Show Gist options
  • Save joubin/be5d0f4b3541d098344c to your computer and use it in GitHub Desktop.
Save joubin/be5d0f4b3541d098344c to your computer and use it in GitHub Desktop.
package so;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
/**
* This program executes the Diffie-Hellman key agreement protocol
* between 2 parties: Alice and Bob.
* <p>
* By default, preconfigured parameters (1024-bit prime modulus and base
* generator used by SKIP) are used.
* If this program is called with the "-gen" option, a new set of
* parameters is created.
*/
public class DHKeyAgreement2 {
private DHKeyAgreement2() {
}
public static void main(String argv[]) {
try {
DHKeyAgreement2 keyAgree = new DHKeyAgreement2();
keyAgree.run();
} catch (Exception e) {
System.err.println("Error: " + e);
System.exit(1);
}
}
private void run() throws Exception {
DHParameterSpec dhSkipParamSpec;
// Some central authority creates new DH parameters
System.out.println
("Creating Diffie-Hellman parameters (takes VERY long) ...");
AlgorithmParameterGenerator paramGen
= AlgorithmParameterGenerator.getInstance("DH");
paramGen.init(1024);
AlgorithmParameters algorithmParameters = paramGen.generateParameters();
dhSkipParamSpec = algorithmParameters.getParameterSpec
(DHParameterSpec.class);
/*
* Alice creates her own DH key pair, using the DH parameters from
* above
*/
System.out.println("Server: Generate DH keypair ...");
KeyPairGenerator serverKeyGenerator = KeyPairGenerator.getInstance("DH");
serverKeyGenerator.initialize(dhSkipParamSpec);
KeyPair serverKeyPair = serverKeyGenerator.generateKeyPair();
// Alice creates and initializes her DH KeyAgreement object
System.out.println("Server: Initialization ...");
KeyAgreement serverKeyAgreement = KeyAgreement.getInstance("DH");
serverKeyAgreement.init(serverKeyPair.getPrivate());
// Alice encodes her public key, and sends it over to Bob.
byte[] serverPublicKeyEncoded = serverKeyPair.getPublic().getEncoded();
/*
* Let's turn over to Bob. Bob has received Alice's public key
* in encoded format.
* He instantiates a DH public key from the encoded key material.
*/
KeyFactory clientKeyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec
(serverPublicKeyEncoded);
PublicKey serverPublicKey = clientKeyFactory.generatePublic(x509KeySpec);
/*
* Bob gets the DH parameters associated with Alice's public key.
* He must use the same parameters when he generates his own key
* pair.
*/
DHParameterSpec dhParamSpec = ((DHPublicKey) serverPublicKey).getParams();
// Bob creates his own DH key pair
System.out.println("Client: Generate DH keypair ...");
KeyPairGenerator clientKeyPairGen = KeyPairGenerator.getInstance("DH");
clientKeyPairGen.initialize(dhParamSpec);
KeyPair clientKeyPair = clientKeyPairGen.generateKeyPair();
// Bob creates and initializes his DH KeyAgreement object
System.out.println("Client: Initialization ...");
KeyAgreement clientKeyAgreement = KeyAgreement.getInstance("DH");
clientKeyAgreement.init(clientKeyPair.getPrivate());
// Bob encodes his public key, and sends it over to Alice.
byte[] clientPublicKeyEncoded = clientKeyPair.getPublic().getEncoded();
/*
* Alice uses Bob's public key for the first (and only) phase
* of her version of the DH
* protocol.
* Before she can do so, she has to instantiate a DH public key
* from Bob's encoded key material.
*/
KeyFactory serverKeyFactory = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(clientPublicKeyEncoded);
PublicKey clientPublicKey = serverKeyFactory.generatePublic(x509KeySpec);
System.out.println("Server: Execute PHASE1 ...");
serverKeyAgreement.doPhase(clientPublicKey, true);
/*
* Bob uses Alice's public key for the first (and only) phase
* of his version of the DH
* protocol.
*/
System.out.println("Client: Execute PHASE1 ...");
clientKeyAgreement.doPhase(serverPublicKey, true);
/*
* At this stage, both Alice and Bob have completed the DH key
* agreement protocol.
* Both generate the (same) shared secret.
*/
byte[] serverSharedSecret = serverKeyAgreement.generateSecret();
byte[] clientSharedSecret = clientKeyAgreement.generateSecret();
//
// System.out.println("Server secret: " +
// toHexString(serverSharedSecret));
// System.out.println("Client secret: " +
// toHexString(clientSharedSecret));
if (!java.util.Arrays.equals(serverSharedSecret, clientSharedSecret))
throw new Exception("Shared secrets differ");
System.out.println("Shared secrets are the same");
/*
* Now let's return the shared secret as a SecretKey object
* and use it for encryption. First, we generate SecretKeys for the
* "DES" algorithm (based on the raw shared secret data) and
* then we use DES in ECB mode
* as the encryption algorithm. DES in ECB mode does not require any
* parameters.
*
* Then we use DES in CBC mode, which requires an initialization
* vector (IV) parameter. In CBC mode, you need to initialize the
* Cipher object with an IV, which can be supplied using the
* javax.crypto.spec.IvParameterSpec class. Note that you have to use
* the same IV for encryption and decryption: If you use a different
* IV for decryption than you used for encryption, decryption will
* fail.
*
* NOTE: If you do not specify an IV when you initialize the
* Cipher object for encryption, the underlying implementation
* will generate a random one, which you have to retrieve using the
* javax.crypto.Cipher.getParameters() method, which returns an
* instance of java.security.AlgorithmParameters. You need to transfer
* the contents of that object (e.g., in encoded format, obtained via
* the AlgorithmParameters.getEncoded() method) to the party who will
* do the decryption. When initializing the Cipher for decryption,
* the (reinstantiated) AlgorithmParameters object must be passed to
* the Cipher.init() method.
*/
System.out.println("Return shared secret as SecretKey object ...");
// Bob
// NOTE: The call to clientKeyAgreement.generateSecret above reset the key
// agreement object, so we call doPhase again prior to another
// generateSecret call
clientKeyAgreement.doPhase(serverPublicKey, true);
SecretKey key1 = DHKeyAgreement2.agreeSecretKey(clientKeyPair.getPrivate(), serverKeyPair.getPublic(),
true);
SecretKey key2 = DHKeyAgreement2.agreeSecretKey( serverKeyPair.getPrivate(), clientKeyPair.getPublic(),
true);
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
// Init the cipher with Alice's key1
c.init(Cipher.ENCRYPT_MODE, key1);
byte[] ciphertext = c.doFinal("Stand and unfold yourself"
.getBytes());
c.init(Cipher.DECRYPT_MODE, key2);
// Decrypts and print
System.out.println("Decrypted: "
+ new String(c.doFinal(ciphertext), "utf-8"));
c = Cipher.getInstance("AES/ECB/PKCS5Padding");
// Init the cipher with Alice's key1
c.init(Cipher.ENCRYPT_MODE, key2);
ciphertext = c.doFinal("Stand and unfold yourself 2"
.getBytes());
c.init(Cipher.DECRYPT_MODE, key1);
// Decrypts and print
System.out.println("Decrypted: "
+ new String(c.doFinal(ciphertext), "utf-8"));
// Alice
// NOTE: The call to serverKeyAgreement.generateSecret above reset the key
// agreement object, so we call doPhase again prior to another
// generateSecret call
// serverKeyAgreement.doPhase(clientPublicKey, true);
// SecretKey aliceDesKey = serverKeyAgreement.generateSecret("AES");
//
//
// /*
// * Bob encrypts, using AES in CBC mode
// */
// Cipher bobCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);
//
// byte[] cleartext = "This is just an example".getBytes();
// byte[] ciphertext = bobCipher.doFinal(cleartext);
// // Retrieve the parameter that was used, and transfer it to Alice in
// // encoded format
// byte[] encodedParams = bobCipher.getParameters().getEncoded();
//
// /*
// * Alice decrypts, using AES in ECB mode
// */
// // Instantiate AlgorithmParameters object from parameter encoding
// // obtained from Bob
// AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
// params.init(encodedParams);
// Cipher aliceCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey, params);
// byte[] recovered = aliceCipher.doFinal(ciphertext);
//
// if (!java.util.Arrays.equals(cleartext, recovered))
// throw new Exception("AES in ECB mode recovered text is " +
// "different from cleartext");
// System.out.println("AES in ECB mode recovered text is " +
// "same as cleartext");
}
public static SecretKey agreeSecretKey(PrivateKey prk_self,
PublicKey pbk_peer, boolean lastPhase) throws Exception {
// instantiates and inits a KeyAgreement
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(prk_self);
// Computes the KeyAgreement
ka.doPhase(pbk_peer, lastPhase);
// Generates the shared secret
byte[] secret = ka.generateSecret();
// === Generates an AES key ===
// you should really use a Key Derivation Function instead, but this is
// rather safe
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] bkey = Arrays.copyOf(
sha256.digest(secret), 128 / Byte.SIZE);
SecretKey desSpec = new SecretKeySpec(bkey, "AES");
return desSpec;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment