4

Disclaimer: I first posted this question on security.stackexchange some minutes ago but deleted it, this is probably a better place for it.

My goal is to use JWE with hybrid encryption (ECDH+AES) for exchanging sensitive data with another party. However, the example code I can find for various Java libraries doesn't match my understanding of how ECDH or asymmetric encryption with EC works in general, which seems to be different to e.g. a hybrid approach with RSA+AES.

My understanding of typical hybrid encryption with RSA and AES:

Sender:

  • create an AES content encryption key (CEK) / session key
  • encrypt the message payload with the CEK
  • encrypt the CEK with the recipient's public RSA key
  • send both the encrypted CEK as well as the encrypted message to the recipient

Recipient:

  • Decrypts the RSA-encrypted CEK with the recipient's private key
  • Decrypts the AES-encrypted message with that CEK

So in theory, it's not even necessary for the sender to have its own key pair, as long as there is no signing involved.

My understanding of how the same is done when using ECDH and AES:

Sender:

  • Derive the shared secret from the sender's EC private key and the recipient's EC public key
  • use that shared secret directly as CEK for AES and encrypt the message (or generate a temporary CEK and encrypt it with the shared secret, a.k.a. keywrap)
  • send the encrypted message to the sender (and optionally the encrypted CEK, in case of key-wrapping)

Recipient:

  • Derive the shared secret from the sender's EC public key and the recipient's EC private key
  • use the shared secret as CEK to AES-decrypt the message (or to decrypt the actual CEK when used with key-wrapping and decrypt the content afterward)

Regardless if it's about encryption or decryption, the derivation of the shared secret on both ends conceptually requires that there are 2 key pairs involved.

However, the code examples for common Java libraries (e.g. Nimbus JOSE) that I can find look like this:

ECKey pubKey = ECKey.parse("...");
JWEEncrypter encrypter = new ECDHEncrypter(pubKey);
JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).build();
String sensitive = "top secret message";
JWEObject jwe = new JWEObject(header, new Payload(sensitive));
jwe.encrypt(encrypter);

I'm not referring to the seemingly missing CEK/AES part, I suppose that's the library doing for me under the hood, but to the way, ECDH is used or not used. It's not just that there is no obvious association between two key pairs for obtaining the shared secret, but there is no second key pair involved altogether.

So either my understanding of ECDH is wrong, or I can't wrap my head around how ECDH is used in the context of JWEs.

Rohit Gupta
  • 489
  • 2
  • 5
  • 10
ceaaj
  • 43
  • 4

1 Answers1

3

You are talking about IES or ECIES in the context of Elliptic Curves. The way it works is that there is a static key pair of the receiver, for which the public key first needs to be trusted by the sender. The sender then creates it's own ephemeral key pair, which is used to perform the key agreement that underlies the protocol.

As the key pair is ephemeral the private key is only used once during the call to encrypt, after which it is destroyed. The way that this is normally programmed is that the private key is a field of the JWEEncrypter instance. It doesn't need to be exported as it only needs to exist until the data encryption key is calculated. As such it is hidden from the user.

That leaves the public key. Normally the user of the library would have to handle that as programmer by attaching it to the encrypted message. However, JWE is a container format. So it doesn't produce just a ciphertext, it wraps it into a structure from which the ephemeral public key can be retrieved. You can find the header specification that contains that key in the RFC. So it is hidden from the user as well.

As the receiver does keep the static private key, it can derive the same symmetric key using the public key that is in the container format. Once the same symmetric data key is derived the contents within the container can be decrypted.


It may be surprising, but key encapsulation using RSA-OAEP and key agreement using ECIES can use the exact same number of messages. Of course there are differences: the encapsulated key must be sent for RSA-OAEP instead of sending the ephemeral public key in case of ECIES, but for a high level API, all things may well look the same.

Maarten Bodewes
  • 96,351
  • 14
  • 169
  • 323