Skip to content

Instantly share code, notes, and snippets.

@zcdziura
Last active July 13, 2023 05:08
Show Gist options
  • Save zcdziura/7652286 to your computer and use it in GitHub Desktop.
Save zcdziura/7652286 to your computer and use it in GitHub Desktop.
Encryption using Elliptic Curves and Diffie-Hellman key exchanges
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;
}
}
Original plaintext message: Look mah, I'm a message!
Encrypted cipher text: 7AFCF3F9A6213FA6900D3DFC12553379580FC7AD362E2C2E28F548FC2AF42F07CF2B057537376F36
Decrypted cipher text: Look mah, I'm a message!
@arabsiphone
Copy link

Fuck u

@Kruxinator
Copy link

Kruxinator commented Mar 20, 2018

replace "BC" with new org.bouncycastle.jce.provider.BouncyCastleProvider() and remove NoSuchProviderException

@isha3026
Copy link

isha3026 commented May 6, 2018

How can i remove this exception??
java.lang.SecurityException: no manifest section for signature file entry org/bouncycastle/jce/provider/JDKMessageDigest$RIPEMD256.class
at sun.security.util.SignatureFileVerifier.verifySection(SignatureFileVerifier.java:608)
at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:341)
at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:263)
at java.util.jar.JarVerifier.processEntry(JarVerifier.java:275)
at java.util.jar.JarVerifier.update(JarVerifier.java:230)
at java.util.jar.JarFile.initializeVerifier(JarFile.java:383)
at java.util.jar.JarFile.getInputStream(JarFile.java:450)
at sun.misc.URLClassPath$JarLoader$2.getInputStream(URLClassPath.java:977)
at sun.misc.Resource.cachedInputStream(Resource.java:77)
at sun.misc.Resource.getByteBuffer(Resource.java:160)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:454)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" C:\Users\user\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 2 seconds)

@sourabhlodha
Copy link

Hi Team ,

I am try to convert KeyPair to String but unfortunately I am not able to convert String to KeyPair. Can you help me out in this.
Below is mention which I try.
KeyPair keyPairA = generateECKeys();
String p= keyPairA.toString();
//Need again to convert p to KeyPair

@Chandu456
Copy link

\Jarfiles>javac Test.java
Test.java:23: error: package org.bouncycastle does not exist
import org.bouncycastle.*;
^
Test.java:24: error: package org.bouncycastle.jce.spec does not exist
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
^
Test.java:54: error: cannot find symbol
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
^
symbol: class ECNamedCurveParameterSpec
location: class Test
Test.java:54: error: cannot find symbol
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
^
symbol: variable ECNamedCurveTable
location: class Test
4 errors
Iam getting these errors please help me out

@tamilairy
Copy link

tamilairy commented Jul 18, 2019

Security Provider "BC" will not work from API level 28.

Try to add SpongyCastle manually:
Security.insertProviderAt(new BouncyCastleProvider(), 1);

add this to your build.gradle dependencies:

/* spongy castle */
implementation "com.madgag.spongycastle:core:1.58.0.0"
implementation "com.madgag.spongycastle:prov:1.58.0.0"

Make sure the BouncyCastleProvider() was coming from spongycastle:
import org.spongycastle.jce.provider.BouncyCastleProvider

@buddingleader
Copy link

Excuse me, I have occurred an error NoSuchAlgorithmException:

    java.lang.RuntimeException: Unable to start service com.example.helloworld.crypto.ecc.ECCService@1732d6 with Intent { cmp=com.example.helloworld/.crypto.ecc.ECCService (has extras) }: java.security.NoSuchAlgorithmException: no such algorithm: ECDH for provider BC
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3346)
        at android.app.ActivityThread.-wrap21(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1585)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6169)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781)
     Caused by: java.security.NoSuchAlgorithmException: no such algorithm: ECDH for provider BC
        at sun.security.jca.GetInstance.getService(GetInstance.java:87)
        at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
        at java.security.KeyPairGenerator.getInstance(KeyPairGenerator.java:286)
        at com.example.helloworld.crypto.ecc.ECDH.generateKeyPair(ECDH.kt:11)
        at com.example.helloworld.crypto.ecc.ECCService.testECDH(ECCService.kt:57)
        at com.example.helloworld.crypto.ecc.ECCService.onStartCommand(ECCService.kt:16)
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3329)
        at android.app.ActivityThread.-wrap21(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1585) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6169) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781

It's my code:

class ECDH {
    fun generateKeyPair(): KeyPair {
        val parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1")
        val keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC")

        keyPairGenerator.initialize(parameterSpec)
        return keyPairGenerator.generateKeyPair()
    }

    fun generateSharedSecret(privateKey: PrivateKey, publicKey: PublicKey): SecretKey? {
        val keyAgreement = KeyAgreement.getInstance("ECDH", "BC")
        keyAgreement.init(privateKey)
        keyAgreement.doPhase(publicKey, true)

        return keyAgreement.generateSecret("AES")
    }
}

private fun testECDH() {
        Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider())
        var providers = Security.getProviders()
        for (providers in providers) {
            println("providers: $providers")
        }

        val ecdh = ECDH()
        val keyPair = ecdh.generateKeyPair()
        val keyPair1 = ecdh.generateKeyPair()
        var secretKey = ecdh.generateSharedSecret(keyPair.private, keyPair1.public)
        var secretKey1 = ecdh.generateSharedSecret(keyPair1.private, keyPair.public)
        println("secretKey:$secretKey")
        println("secretKey1:$secretKey1")
    }

@buddingleader
Copy link

\Jarfiles>javac Test.java
Test.java:23: error: package org.bouncycastle does not exist
import org.bouncycastle.*;
^
Test.java:24: error: package org.bouncycastle.jce.spec does not exist
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
^
Test.java:54: error: cannot find symbol
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
^
symbol: class ECNamedCurveParameterSpec
location: class Test
Test.java:54: error: cannot find symbol
ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1");
^
symbol: variable ECNamedCurveTable
location: class Test
4 errors
Iam getting these errors please help me out

You can download it at https://www.bouncycastle.org/latest_releases.html, and then put it in you project libs.

@buddingleader
Copy link

Excuse me, I have occurred an error NoSuchAlgorithmException:

    java.lang.RuntimeException: Unable to start service com.example.helloworld.crypto.ecc.ECCService@1732d6 with Intent { cmp=com.example.helloworld/.crypto.ecc.ECCService (has extras) }: java.security.NoSuchAlgorithmException: no such algorithm: ECDH for provider BC
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3346)
        at android.app.ActivityThread.-wrap21(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1585)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6169)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781)
     Caused by: java.security.NoSuchAlgorithmException: no such algorithm: ECDH for provider BC
        at sun.security.jca.GetInstance.getService(GetInstance.java:87)
        at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
        at java.security.KeyPairGenerator.getInstance(KeyPairGenerator.java:286)
        at com.example.helloworld.crypto.ecc.ECDH.generateKeyPair(ECDH.kt:11)
        at com.example.helloworld.crypto.ecc.ECCService.testECDH(ECCService.kt:57)
        at com.example.helloworld.crypto.ecc.ECCService.onStartCommand(ECCService.kt:16)
        at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3329)
        at android.app.ActivityThread.-wrap21(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1585) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6169) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781

It's my code:

class ECDH {
    fun generateKeyPair(): KeyPair {
        val parameterSpec = ECNamedCurveTable.getParameterSpec("brainpoolp256r1")
        val keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC")

        keyPairGenerator.initialize(parameterSpec)
        return keyPairGenerator.generateKeyPair()
    }

    fun generateSharedSecret(privateKey: PrivateKey, publicKey: PublicKey): SecretKey? {
        val keyAgreement = KeyAgreement.getInstance("ECDH", "BC")
        keyAgreement.init(privateKey)
        keyAgreement.doPhase(publicKey, true)

        return keyAgreement.generateSecret("AES")
    }
}

private fun testECDH() {
        Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider())
        var providers = Security.getProviders()
        for (providers in providers) {
            println("providers: $providers")
        }

        val ecdh = ECDH()
        val keyPair = ecdh.generateKeyPair()
        val keyPair1 = ecdh.generateKeyPair()
        var secretKey = ecdh.generateSharedSecret(keyPair.private, keyPair1.public)
        var secretKey1 = ecdh.generateSharedSecret(keyPair1.private, keyPair.public)
        println("secretKey:$secretKey")
        println("secretKey1:$secretKey1")
    }

I solved the problem,
change KeyPairGenerator.getInstance("ECDH", "BC") to KeyPairGenerator.getInstance("ECDH", org.bouncycastle.jce.provider.BouncyCastleProvider())

@srahulk1
Copy link

srahulk1 commented Jan 27, 2020

@ 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.

@simranss
Copy link

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