Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elliptic curve key wrapping/encapsulation using Java

From my research I learned that this is the correct way to wrap an AES session key using RSA:

import java.security.*;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class Main {
    public static void main(String args[]) throws Exception {
        KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES");
        aesKeyGen.init(256);
        SecretKey secretKey = aesKeyGen.generateKey();

        Base64.Encoder encoder = Base64.getEncoder().withoutPadding();
        Cipher cipher = Cipher.getInstance("RSA");
        KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA");
        rsaKeyGen.initialize(4096);
        KeyPair keyPair = rsaKeyGen.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        cipher.init(Cipher.WRAP_MODE, publicKey);
        byte[] wrappedKey = cipher.wrap(secretKey);
        cipher.init(Cipher.UNWRAP_MODE, privateKey);
        SecretKey unwrappedKey = (SecretKey)cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);

        System.out.println(encoder.encodeToString(wrappedKey));
        System.out.println(encoder.encodeToString(secretKey.getEncoded()));
        System.out.println(encoder.encodeToString(unwrappedKey.getEncoded()));
    }
}

My questions are:

  • Should I keep using Cipher.getInstance("RSA"), switch to Cipher.getInstance("RSA/ECB/PKCS1Padding") or something else?
  • How can I do the same thing I am doing above but using EC?
  • Is key wrapping the same as key encapsulation?
  • Do I need to clear the memory used by secretKey in some way like we do with arrays (e.g. Arrays.fill(array, (byte)0)) or just if call getEncoded()?

To answer my second question I found that I should use:

KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC");

However I am not sure what to use here instead of <name>:

Cipher cipher = Cipher.getInstance("<name>");

I also discovered that I can choose what curve to use by doing this:

...
ECGenParameterSpec ecsp = new ECGenParameterSpec("sect571k1"); 
ecKeyGen.initialize(571);
...

but again, I don't know what parameter should I use here instead of <name>:

new ECGenParameterSpec("<name>");

I believe that some good candidates are the following:

secp521r1, nistp521, sect571r1, nistb571, nistb571, sect571k1, nistk571 nistk571, sect163r2

are there other options? Can you help me select the best one? Is it enough to instantiate an ECGenParameterSpec object to set the initialization parameters of the EC KeyPairGenerator?

like image 984
Nerva Avatar asked Jun 23 '26 23:06

Nerva


1 Answers

Should I keep using Cipher.getInstance("RSA"), switch to Cipher.getInstance("RSA/ECB/PKCS1Padding") or something else?

You should switch to the fully specified string at the minimum; different providers could possible have different defaults, and specifying the full scheme is simply easier to read/maintain; not everybody will know the defaults.

PKCS#1 v1.5 padding is however susceptible to certain padding oracle attacks to switching to OAEP padding would provide certain security benefits.

How can I do the same thing I am doing above but using EC?

Not easily. The RSA problem allows for both signature generation and encryption, while the (EC)DH problem allows for signature generation and Diffie-Hellman. It is however possible to use ECDH to implement ECIES, which "derives" a symmetric key which is then used to encrypt the message.

ECIES is not included in the normal Java runtimes, but Bouncy Castle has implementations and it is of course possible to build the functionality on top of ECDH, which is included into the KeyAgreement class.

Is key wrapping the same as key encapsulation?

They are related. Key encapsulation is more for session keys and often derives the session key rather than encrypting it. I.e. you cannot use key encapsulation to (directly) encrypt an existing key if this is the case. Key wrapping does encrypt existing keys and may use specialized (deterministic) encryption schemes - although this is not directly the case with the schemes provides by the Java runtime.

The wrapping in Java is mainly useful because you don't need to store the intermediate key values of the key being wrapped in a byte array; this would be impossible if the keys are managed in hardware (such as a smart card) anyway.

Do I need to clear the memory used by secretKey in some way like we do with arrays (e.g. Arrays.fill(array, (byte)0)) or just if call getEncoded()?

You can't as the fields are not directly accessible. Java 8 and later implement Destroyable.destroy() so you have a method to destroy the keys. If the keys are in hardware then this method may throw an exception though.

I believe that some good candidates are the following:

secp521r1, nistp521, sect571r1, nistb571, nistb571, sect571k1, nistk571 nistk571, sect163r2

are there other options?

Sure, for lower number of bits: secp384r1 and secp256r1 for instance.

Can you help me select the best one?

Define best. Performance vs security, but even secp256r1 is already pretty strong. I would choose a SEC prime (p) curve though, not a Koblenz or twisted curve (k or t curve).

Is it enough to instantiate an ECGenParameterSpec object to set the initialization parameters of the EC KeyPairGenerator?

Yes.

like image 151
Maarten Bodewes Avatar answered Jun 25 '26 12:06

Maarten Bodewes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!