Created
March 9, 2016 03:07
-
-
Save joubin/be5d0f4b3541d098344c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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