Last active
July 13, 2023 05:08
-
-
Save zcdziura/7652286 to your computer and use it in GitHub Desktop.
Encryption using Elliptic Curves and Diffie-Hellman key exchanges
This file contains 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
import java.io.UnsupportedEncodingException; | |
import java.security.InvalidAlgorithmParameterException; | |
import java.security.InvalidKeyException; | |
import java.security.Key; | |
import java.security.KeyPair; | |
import java.security.KeyPairGenerator; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.NoSuchProviderException; | |
import java.security.PrivateKey; | |
import java.security.PublicKey; | |
import java.security.SecureRandom; | |
import java.util.Enumeration; | |
import javax.crypto.BadPaddingException; | |
import javax.crypto.Cipher; | |
import javax.crypto.IllegalBlockSizeException; | |
import javax.crypto.KeyAgreement; | |
import javax.crypto.NoSuchPaddingException; | |
import javax.crypto.SecretKey; | |
import javax.crypto.ShortBufferException; | |
import javax.crypto.spec.IvParameterSpec; | |
import javax.crypto.spec.SecretKeySpec; | |
import org.bouncycastle.jce.ECNamedCurveTable; | |
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; | |
public class Test { | |
public static byte[] iv = new SecureRandom().generateSeed(16); | |
public static void main(String[] args) { | |
String plainText = "Look mah, I'm a message!"; | |
System.out.println("Original plaintext message: " + plainText); | |
// Initialize two key pairs | |
KeyPair keyPairA = generateECKeys(); | |
KeyPair keyPairB = generateECKeys(); | |
// Create two AES secret keys to encrypt/decrypt the message | |
SecretKey secretKeyA = generateSharedSecret(keyPairA.getPrivate(), | |
keyPairB.getPublic()); | |
SecretKey secretKeyB = generateSharedSecret(keyPairB.getPrivate(), | |
keyPairA.getPublic()); | |
// Encrypt the message using 'secretKeyA' | |
String cipherText = encryptString(secretKeyA, plainText); | |
System.out.println("Encrypted cipher text: " + cipherText); | |
// Decrypt the message using 'secretKeyB' | |
String decryptedPlainText = decryptString(secretKeyB, cipherText); | |
System.out.println("Decrypted cipher text: " + decryptedPlainText); | |
} | |
public static KeyPair generateECKeys() { | |
try { | |
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1"); | |
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( | |
"ECDH", "BC"); | |
keyPairGenerator.initialize(parameterSpec); | |
KeyPair keyPair = keyPairGenerator.generateKeyPair(); | |
return keyPair; | |
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | |
| NoSuchProviderException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static SecretKey generateSharedSecret(PrivateKey privateKey, | |
PublicKey publicKey) { | |
try { | |
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC"); | |
keyAgreement.init(privateKey); | |
keyAgreement.doPhase(publicKey, true); | |
SecretKey key = keyAgreement.generateSecret("AES"); | |
return key; | |
} catch (InvalidKeyException | NoSuchAlgorithmException | |
| NoSuchProviderException e) { | |
// TODO Auto-generated catch block | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static String encryptString(SecretKey key, String plainText) { | |
try { | |
IvParameterSpec ivSpec = new IvParameterSpec(iv); | |
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); | |
byte[] plainTextBytes = plainText.getBytes("UTF-8"); | |
byte[] cipherText; | |
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); | |
cipherText = new byte[cipher.getOutputSize(plainTextBytes.length)]; | |
int encryptLength = cipher.update(plainTextBytes, 0, | |
plainTextBytes.length, cipherText, 0); | |
encryptLength += cipher.doFinal(cipherText, encryptLength); | |
return bytesToHex(cipherText); | |
} catch (NoSuchAlgorithmException | NoSuchProviderException | |
| NoSuchPaddingException | InvalidKeyException | |
| InvalidAlgorithmParameterException | |
| UnsupportedEncodingException | ShortBufferException | |
| IllegalBlockSizeException | BadPaddingException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static String decryptString(SecretKey key, String cipherText) { | |
try { | |
Key decryptionKey = new SecretKeySpec(key.getEncoded(), | |
key.getAlgorithm()); | |
IvParameterSpec ivSpec = new IvParameterSpec(iv); | |
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); | |
byte[] cipherTextBytes = hexToBytes(cipherText); | |
byte[] plainText; | |
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, ivSpec); | |
plainText = new byte[cipher.getOutputSize(cipherTextBytes.length)]; | |
int decryptLength = cipher.update(cipherTextBytes, 0, | |
cipherTextBytes.length, plainText, 0); | |
decryptLength += cipher.doFinal(plainText, decryptLength); | |
return new String(plainText, "UTF-8"); | |
} catch (NoSuchAlgorithmException | NoSuchProviderException | |
| NoSuchPaddingException | InvalidKeyException | |
| InvalidAlgorithmParameterException | |
| IllegalBlockSizeException | BadPaddingException | |
| ShortBufferException | UnsupportedEncodingException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
public static String bytesToHex(byte[] data, int length) { | |
String digits = "0123456789ABCDEF"; | |
StringBuffer buffer = new StringBuffer(); | |
for (int i = 0; i != length; i++) { | |
int v = data[i] & 0xff; | |
buffer.append(digits.charAt(v >> 4)); | |
buffer.append(digits.charAt(v & 0xf)); | |
} | |
return buffer.toString(); | |
} | |
public static String bytesToHex(byte[] data) { | |
return bytesToHex(data, data.length); | |
} | |
public static byte[] hexToBytes(String string) { | |
int length = string.length(); | |
byte[] data = new byte[length / 2]; | |
for (int i = 0; i < length; i += 2) { | |
data[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4) + Character | |
.digit(string.charAt(i + 1), 16)); | |
} | |
return data; | |
} | |
} |
This file contains 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
Original plaintext message: Look mah, I'm a message! | |
Encrypted cipher text: 7AFCF3F9A6213FA6900D3DFC12553379580FC7AD362E2C2E28F548FC2AF42F07CF2B057537376F36 | |
Decrypted cipher text: Look mah, I'm a message! |
@ zcdziura Hi, I just wanted to understand the example which you have shared uses an asymmetric form of encryption or symmetric. I am asking this question as to when I implemented this solution and after generating a shared secret I am getting both the key as same. So I am a bit doubtful about this. It will be great if you can help me to understand this.
The private and public keys are the same in the keypair.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I solved the problem,
change
KeyPairGenerator.getInstance("ECDH", "BC")
toKeyPairGenerator.getInstance("ECDH", org.bouncycastle.jce.provider.BouncyCastleProvider())