How to perform encryption and decryption in java using secp256r1 Elliptical Curve key pair?

1.4k views Asked by At

We need to perform encryption/decryption of some string message in java using EC key pair. key pair has been generated using secp256r1 Elliptical Curve. We just want to use java core utilities.

Encrypting code - in java server side Decryption code - it suppose to be at android and ios Key - Asymmetric EC key pair, public key readily available at java encryption side and private key readily available at android/ios decryption side. We need not to worry about key distribution, mechanism already there.

Due to encryption and decryption code present at cross platform, we want to avoid any third party library as it might not available across all three platforms - java, android, ios

ECIES, kind of encryption will work for us but how to do it in plain java utilities or with some library whihc is available across all three platforms - java, android, ios

My Sample Code(java encryption and decryption) -

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.util.encoders.Hex;

public class Main {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
        keyGen.initialize(new ECGenParameterSpec("secp256r1"), new SecureRandom());
        KeyPair ecKeyPair = keyGen.generateKeyPair();
        

        Cipher iesCipher = Cipher.getInstance("AES/CBC/NoPadding");
        Cipher iesDecipher = Cipher.getInstance("AES/CBC/NoPadding");
        iesCipher.init(Cipher.ENCRYPT_MODE, ecKeyPair.getPublic());

        String message = "Hello World";

        byte[] ciphertext = iesCipher.doFinal(message.getBytes());
        System.out.println(Hex.toHexString(ciphertext));

        iesDecipher.init(Cipher.DECRYPT_MODE, ecKeyPair.getPrivate(), iesCipher.getParameters());
        byte[] plaintext = iesDecipher.doFinal(ciphertext);

        System.out.println(new String(plaintext));
    }

    public void testEncryptDecrypt() throws Exception {}

}

but above code throwing me error -

Exception in thread "main" java.security.InvalidKeyException: No installed provider supports this key: sun.security.ec.ECPublicKeyImpl 

at line - iesCipher.init(Cipher.ENCRYPT_MODE, ecKeyPair.getPublic());

Please suggest what i am missing here.

1

There are 1 answers

3
Topaco On

You can't just use an EC key for AES.

One way to encrypt in the EC context is ECIES, a hybrid encryption scheme, which essentially uses ECDH to agree on a symmetric key that is used to symmetrically encrypt/decrypt the data. As far as I know ECIES is not possible in Java with built-in functionalities alone, i.e. you need a third party library like BouncyCastle.

Unlike ECIES, ECDH can be implemented with Java onboard means. This allows the agreement on a symmetric key, as in ECIES, which is then used to perform symmetric encryption/decryption. Since you don't want to use a third-party library, this would be a possible way:

// Generate EC keys for Alice and Bob
// as already implemented in your code

// Exchange public keys

// Derive AES key for Alice
SecretKey secretKeyAlice = getKeyViaECDH(ecKeyPairAlice.getPrivate(), ecKeyPairBob.getPublic());  // identical with secretKeyBob
System.out.println(Base64.getEncoder().encodeToString(secretKeyAlice.getEncoded()));
    
// Derive AES key for Bob
SecretKey secretKeyBob = getKeyViaECDH(ecKeyPairBob.getPrivate(), ecKeyPairAlice.getPublic()); // identical with secretKeyAlice
System.out.println(Base64.getEncoder().encodeToString(secretKeyBob.getEncoded()));

// Symmetric encryption/decryption with AES/CBC
// as already implemented in your code, but 
// - use secretKeyAlice and secretKeyBob for encryption/decryption
// - apply PKCS5Padding if the plaintext size is not an integer multiple of the block size, 16 bytes for AES

with

private static SecretKey getKeyViaECDH(PrivateKey privKey, PublicKey pubKey) throws Exception {
    KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
    keyAgreement.init(privKey);
    keyAgreement.doPhase(pubKey, true);
    byte[] sharedSecret = keyAgreement.generateSecret();
    byte[] key = MessageDigest.getInstance("SHA-256").digest(sharedSecret); // Derive key as SHA256 hash from shared secret
    SecretKey secretKey = new SecretKeySpec(key, "AES");
    return secretKey;
}

Please note that this implementation does not yet include the authentication of the ciphertext (as e.g. ECIES does). This can be implemented with a MAC, alternatively a mode like GCM can be used which performs authentication implicitly.