3

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?

Nerva
  • 329
  • 4
  • 13

1 Answers1

4

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.

Community
  • 1
  • 1
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • _ECIES is not included in the normal Java runtimes_: actually, it is a supported/required algorithm name for the JCE's `Cipher.getInstance()` routine. I still haven't figured out how to use it to wrap an existing symmetric session secret key with an EC-created public key, but at least the name exists. – Ti Strga Oct 07 '19 at 17:55
  • @TiStrga It is in the list, but it is ill defined, and it is *not included* in the provider that offers elliptic curve cryptography that comes with the JDK. You may have to include something like Bouncy Castle. But even then, as the algorithm is ill defined, you're better off doing it yourself using `KeyAgreement` followed by a cipher of your choosing. – Maarten Bodewes Oct 08 '19 at 02:45
  • True, it's very under-specified, and I've never gotten the ECIES `Cipher` to do anything but throw exceptions. I'm hesitant to try doing anything with `KeyAgreement` because I don't understand where the second key would be coming from in our own scenario. (I wish I could find some documentation and examples; quite likely it's too different from the original questioner's circumstances.) – Ti Strga Oct 08 '19 at 19:09
  • @TiStrga Would the latter part of [this answer](https://stackoverflow.com/a/58089797/589259) help you? – Maarten Bodewes Oct 08 '19 at 20:41
  • It definitely did, thank you so much! There just doesn't seem to be a way to make EC `KeyAgreement` work alongside other types of key wrapping, existing session keys, and multiple parties' keys -- at least not without getting dangerously "inventive" with key handling. We're shelving the project for now. – Ti Strga Oct 18 '19 at 21:01