title: ECDH + JWE encryption/decryption using jose4j
body: <p>I am trying to implement ECDH encryption/decryption along with JWE in Android (Java).<br>
  I have found the <a href="https://bitbucket.org/b_c/jose4j/wiki/Home" rel="nofollow noreferrer">jose4j</a> and <a href="https://connect2id.com/products/nimbus-jose-jwt" rel="nofollow noreferrer">Nimbus JOSE</a> libraries that aim to do everything I need but appears that it's more challenging than I thought. </p>
  
  <p>If anybody is familiar then it's for 3D Secure 2.0... </p>
  
  <p><strong>In the spec below:</strong></p>
  
  <ul>
  <li>SDK = the <em>local</em> side  </li>
  <li>DS = Directory Server (the <em>other</em> side)  </li>
  </ul>
  
  <p><strong>Next is the spec:</strong> </p>
  
  <blockquote>
    <ul>
    <li>Given: P(DS) - an EC public key (provided in PEM format, can be transformed to PublicKey or to JWK)</li>
    <li>Generate a fresh ephemeral key pair (Q(SDK), d(SDK))</li>
    <li>Conduct a Diffie-Hellman key exchange process according to JWA (RFC7518) in Direct Key Agreement mode using curve P-256, d(SDK) and P(DS) to produce a CEK. The parameter values supported in this version of the specification are:
    
    <ul>
    <li>"alg":ECDH-ES</li>
    <li>"apv":DirectoryServerID</li>
    <li>"epk":P(DS),inJSONWebKey(JWK)format {"kty":"EC", "crv":"P-256"}</li>
    <li>All other parameters: not present</li>
    </ul></li>
    <li>CEK:"kty":oct-256bits</li>
    <li>Generate 128-bit random data as IV</li>
    <li>Encrypt the JSON object according to JWE (RFC7516) using the CEK and JWE Compact Serialization. The parameter values supported in this version of the specification are:
    
    <ul>
    <li>"alg":dir</li>
    <li>"epk":Q(SDK) as {"kty": "EC", "crv": "P-256"}</li>
    <li>"enc":either"A128CBC-HS256"or"A128GCM"</li>
    <li>All other parameters: not present</li>
    </ul></li>
    <li>If the algorithm is A128CBC-HS256 use the full CEK or if the algorithm is A128GCM use the leftmost 128 bits of the CEK.</li>
    <li>Delete the ephemeral key pair (Q(SDK),d(SDK))</li>
    <li>Makes the resulting JWE available to the 3DS Server as SDK Encrypted Data</li>
    </ul>
  </blockquote>
  
  <p>If someone has implemented this exact spec and can share the code this would be brilliant!! </p>
  
  <p>There's an example of creating JWT using ECDH in the examples of jose4j:<br>
  <a href="https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples" rel="nofollow noreferrer">https://bitbucket.org/b_c/jose4j/wiki/JWT%20Examples</a> (the last example, titled as "Producing and consuming a nested (signed and encrypted) JWT").<br>
  But this example is not exactly what I need. It creates a token while I need to encrypt a text. </p>
  
  <p>Starting from "CEK:"kty":oct-256bits" in the spec above I don't understand what to do.</p>
  
  <p>Here's my code (so far) using Nimbus lib: </p>
  
  <pre><code>public String nimbus_encrypt(String plainJson, ECPublicKey otherPublicKey, String directoryServerId) throws JOSEException {
      JWEHeader jweHeader = new JWEHeader(
              JWEAlgorithm.ECDH_ES,
              EncryptionMethod.A128CBC_HS256,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              Base64URL.encode(directoryServerId),
              null,
              0,
              null,
              null,
              null,
              null);
  
      JWEObject jwe = new JWEObject(jweHeader, new Payload(plainJson));
      jwe.encrypt(new ECDHEncrypter(otherPublicKey));
      String serializedJwe = jwe.serialize();
  
      Log.d("[ENCRYPTION]", "nimbus_encrypt: jwe = " + jwe.getHeader());
      Log.d("[ENCRYPTION]", "nimbus_encrypt: serializedJwe = " + serializedJwe);
  
      return serializedJwe;
  }
  </code></pre>
  
  <p>This is the nimbus output: </p>
  
  <blockquote>
    <p>nimbus_encrypt: jwe = {"epk":{"kty":"EC","crv":"P-256","x":"AS0GRfAOWIDONXxaPR_4IuNHcDIUJPHbACjG5L7x-nQ","y":"xonFn1vRASKUTdCkFTwsl16LRmSe-bAF8EO4-mh1NYw"},"apv":"RjAwMDAwMDAwMQ","enc":"A128CBC-HS256","alg":"ECDH-ES"}</p>
    
    <p>nimbus_encrypt: serializedJwe = eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJBUzBHUmZBT1dJRE9OWHhhUFJfNEl1TkhjRElVSlBIYkFDakc1TDd4LW5RIiwieSI6InhvbkZuMXZSQVNLVVRkQ2tGVHdzbDE2TFJtU2UtYkFGOEVPNC1taDFOWXcifSwiYXB2IjoiUmpBd01EQXdNREF3TVEiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiRUNESC1FUyJ9..Pi48b7uj3UilvVXKewFacg.0sx9OkHxxtZvkVm-IENRFw.bu5GvOAwcZxdxaDKWIBqwA</p>
  </blockquote>
  
  <p>Here's my code (so far, using @Brian-Campbell's answer) using jose4j lib: </p>
  
  <pre><code>public String jose4j_encrypt(String plainJson, PublicKey otherPublicKey, String directoryServerId) throws JoseException {
      JsonWebEncryption jwe = new JsonWebEncryption();
      jwe.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES);
      jwe.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_CBC_HMAC_SHA_256);
  
      jwe.setHeader(HeaderParameterNames.AGREEMENT_PARTY_V_INFO, Base64Url.encodeUtf8ByteRepresentation(directoryServerId));
      jwe.setKey(otherPublicKey);
      jwe.setPayload(plainJson);
  
      String serializedJwe = jwe.getCompactSerialization();
      Log.d("[ENCRYPTION]", "jose4j_encrypt: jwe = " + jwe);
      Log.d("[ENCRYPTION]", "jose4j_encrypt: serializedJwe = " + serializedJwe);
  
      return serializedJwe;
  }
  </code></pre>
  
  <p>This is the jose4j output: </p>
  
  <blockquote>
    <p>jose4j_encrypt: jwe = JsonWebEncryption{"alg":"ECDH-ES","enc":"A128CBC-HS256","apv":"RjAwMDAwMDAwMQ","epk":{"kty":"EC","x":"prvyhexJXDWvPQmPA1xBjY8mkHEbrEiJ4Dr-7_5YfdQ","y":"fPjw8UdfzgkVTppPSN5o_wprItKLwecoia9yrWi38yo","crv":"P-256"}}</p>
    
    <p>jose4j_encrypt: serializedJwe = eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImFwdiI6IlJqQXdNREF3TURBd01RIiwiZXBrIjp7Imt0eSI6IkVDIiwieCI6InBydnloZXhKWERXdlBRbVBBMXhCalk4bWtIRWJyRWlKNERyLTdfNVlmZFEiLCJ5IjoiZlBqdzhVZGZ6Z2tWVHBwUFNONW9fd3BySXRLTHdlY29pYTl5cldpMzh5byIsImNydiI6IlAtMjU2In19..gxWYwFQSOqLk5HAgs7acdA.mUIHBiWpWSlQaEOJ_EZGYA.eiTe-88fw-Jfuhji_W0rtg</p>
  </blockquote>
  
  <p>As can be seen the "alg" header in the final result is "ECDH-ES" and not "dir" as required. </p>
  
  <p>If I would implement both sides of the communication then it would be enough, but with this spec seems like many configurations are missing here...</p>
  
  <p>Code using jose4j is longer and seems more configurable but I couldn't construct something valuable enough to post here.</p>
  
  <p>The main missing part for me is how to generate the CEK from the spec above.</p>
  
  <p>Thank you.</p>
  
  <p><strong>EDIT</strong><br>
  Added jose4j code above and added the outputs...</p>