-1

I have this code which encrypts and decrypts text messages using Elliptic curve cryptography simultaneously. I divide split the code into two parts: encryption and decryption. But during decryption I get errors. Can someone resolve those for me.

Shared code:

import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.Scanner;
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.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;

public class ENCRYPT {

  public static byte[] iv = new SecureRandom().generateSeed(16);

  public static void main(String[] args) {
    System.out.println("IV :" + iv);

    Scanner sc = new Scanner(System.in); // object for scanner

    System.out.println("Enter your Message:");
    String plainText = sc.nextLine();

    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);

    String encodedKeyA = Base64.getEncoder().encodeToString(secretKeyA.getEncoded());
    String encodedKeyB = Base64.getEncoder().encodeToString(secretKeyB.getEncoded());

    // Decrypt the message using 'secretKeyB'
    String decryptedPlainText = decryptString(secretKeyB, cipherText);
    System.out.println("Decrypted cipher text: " + decryptedPlainText);
    System.out.println("Secret Key A: " + encodedKeyA);
    System.out.println("Secret Key B: " + encodedKeyB);

  }

  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;
  }
}

Decryption code:

//Decrypt

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.util.Base64;
import java.util.Scanner;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
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 Decrypt {

  public static byte[] iv = new SecureRandom().generateSeed(16);


  public static void main(String[] args) {

    /*  Scanner scc = new Scanner(System.in);
        System.out.println("Enter IV:");
        String ivate = scc.nextLine();
        byte[] iv = ivate.getBytes();
    */
    System.out.println("IV=" + iv);

    Scanner sc = new Scanner(System.in); // object for scanner
    System.out.println("Enter your Cipher:");
    String cipherText = sc.nextLine();

    Scanner scanner = new Scanner(System.in); // object for scanner

    System.out.println("Enter your Secret Key B:");

    String encodedKeyB = scanner.nextLine();
    byte[] decodedKeyB = Base64.getDecoder().decode(encodedKeyB);

    SecretKey secretKeyB = new SecretKeySpec(decodedKeyB, 0, decodedKeyB.length, "AES");

    // 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);
    System.out.println(" cipher text: " + cipherText);
    System.out.println("Key: " + secretKeyB);
  }

  /*  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 secretkeyB, 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 secretkeyB, String cipherText) {
    try {
      Key decryptionKey = new SecretKeySpec(secretkeyB.getEncoded(),
        secretkeyB.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;
  }
}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
Rajkeen
  • 13
  • 1
  • 2
  • 1
    Please provide a [mcve]. – Luke Joshua Park Apr 16 '17 at 06:45
  • Code to decrypt the string shows errors. – Rajkeen Apr 16 '17 at 06:55
  • Actually, you need three `main` methods not just two. In order for the two parties to communicate, they have to know their own key pair and the public key of the other. For that, you would need to serialize the ECDH keys and deserialize them when you actually need to encrypt and decrypt. In other words, you need a setup phase, then an encrypt and decrypt phase. – Artjom B. Apr 16 '17 at 06:58
  • @ArtjomB. your methods seems correct, so I need to encrypt the data using agreed secret by both.Iam I getting you point? – Rajkeen Apr 16 '17 at 07:35
  • No, each party has to derive the shared secret key separately from the public and private key combinations. That's at least the idea behind diffie-hellman – Artjom B. Apr 16 '17 at 07:37

1 Answers1

3

Artjom B is completely right with regards to noting that the key generation needs to take place for each party separately. What you need to do is to generate one class that represents either party or that just contains the shared methods such as generateKeyPair, deriveSecretKey and of course receivePublicKey. You should be able to use that for both the Sender and Receiver classes.

Just typing in the secret key completely destroys the idea of performing key agreement in the first place.


However, this is not the issue with your code.

You are using a static random IV during decryption. An IV should be random during encryption and then communicated with the party decrypting the ciphertext. This is normally accomplished by prefixing the IV to the ciphertext.

You need to generate the IV within the encryption method instead of making it static. Reusing an IV the way you do completely destroys confidentiality for GCM mode as it uses CTR mode underneath.

Note that the GCM mode IV should be 12 bytes, not 16. GCM mode IV's may just be unique (a nonce, number-used-once) rather than random.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • This is not the right place for a code review, so I just noted the most generic issues. I mainly answered because except for above problems you do a lot of things right as well (such as using GCM and explicit encoding). – Maarten Bodewes Apr 16 '17 at 11:25