JWE - PartyUInfo Header ("apu") to contain Ephemeral Public Key

Issue #202 closed
Jack created an issue

We have requirement to create JWE as per the NIST standard with ECDH-ES algorithm (https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf), which requires PartyUInfo header (“apu“) to contain Ephemeral Public Key (epk) as shown below:

<prefix bytes length>||<prefix bytes>||<epk bytes length>||<epk bytes>

But the only public method available in the library is : EcdhKeyAgreementAlgorithm.manageForEncrypt() which accepts headers, but generates a random Ephemeral key. So “apu” header can not contain the ephemeral public key.

Is it possible to expose the other non-public method manageForEncrypt which accepts ephemeral key, as a public method; so that we can generate ephemeral key, add it to “apu“ header and call this method by passing our own generated ephemeral key?

Comments (6)

  1. Brian Campbell repo owner

    We could make the other manageForEncrypt method public. But are you using EcdhKeyAgreementAlgorithm directly rather than JsonWebEncryption? The JsonWebEncryption class is more the expected usage for JWE. And making that method public on EcdhKeyAgreementAlgorithm wouldn’t really help when using JsonWebEncryption.

    Also, could you point me to the where that NIST document requires that PartyUInfo contain the Ephemeral Public Key? I didn’t read all 139 pages (and probably wouldn’t understand a good bit of it even if I did) but I couldn’t find that. As far as I can tell, it just has contain some public information about party U (the sender).

  2. Jack reporter

    Thanks for the prompt response. You are right, it will not help us if we use JsonWebEncryption. We were planning to directly use EcdhKeyAgreementAlgorithm and realized that we can not add ephemeral public key to the “apu“ header, even if we directly use this class.

    I too don’t fully understand the NIST document. We are integrating with our vendor and they said that 'apu' header must contain the ephemeral public key for security.

    Here is the excerpt from the document, which talks about this:

    Page 49 #

    PartyUInfo: A required non-null subfield containing public information about party U. At a minimum, PartyUInfo shall include IDU, an identifier for party U, as a distinct item of information. This subfield could also include information about the public key(s) contributed to the key-agreement transaction by party U.

    Page 121 #

    In addition to identifiers, the inclusion of other context-specific information in the key-derivation input data can be used to draw finer distinctions between key-agreement transactions, providing assurance that parties will not derive the same keying material unless they agree on all of the included information. This can protect against attacks that rely on confusion concerning the context in which key-establishment takes place and/or how the derived keying material is to be used, see [CMU 2009]. Examples of additional context-specific information include (but are not limited to) the protocol employing the key-derivation method, protocol-defined session numbers, the key-agreement scheme that was employed to produce the shared secret Z, any ephemeral public keys and/or nonces exchanged during the key-agreement transaction, the bit length of the derived keying material, and its intended use.

  3. Brian Campbell repo owner

    Yeah, those excerpts are (AFAICT) about key-agreement in general and say that ephemeral public keys could be part of key-derivation input. Which is not the same as requiring it. I’m a little skeptical of your vendor’s requirement, to be honest.

    I would like to help though. But working with EcdhKeyAgreementAlgorithm directly and needing changes to this library doesn’t seem like it’ll really help anybody. So I’m gonna suggest a workaround that basically overrides the headers object to know when epk has been set and then set apu accordingly.

    Here’s some code that shows kinda how that’d work:

        public void meh() throws Exception
        {
            EllipticCurveJsonWebKey recipientJwk  = EcJwkGenerator.generateJwk(EllipticCurves.P256);
    
            JsonWebEncryption jweObject = new SpecialJWE();
            jweObject.setAlgorithmHeaderValue(KeyManagementAlgorithmIdentifiers.ECDH_ES);
            jweObject.setEncryptionMethodHeaderParameter(ContentEncryptionAlgorithmIdentifiers.AES_128_GCM);
            jweObject.setKey(recipientJwk.getPublicKey());
            jweObject.setPayload("party who? party U");
            String jwe = jweObject.getCompactSerialization();
            System.out.println(jwe);
    
            jweObject = new JsonWebEncryption();
            jweObject.setCompactSerialization(jwe);
            jweObject.setKey(recipientJwk.getPrivateKey());
            String payload = jweObject.getPayload();
            System.out.println(payload);
        }
    
        static class SpecialJWE extends JsonWebEncryption {
            public SpecialJWE() {
                this.headers = new SpecialHeaders();
            }
        }
    
        static class SpecialHeaders extends Headers {
            @Override
            public void setJwkHeaderValue(String name, JsonWebKey jwk) {
                super.setJwkHeaderValue(name, jwk);
                if ("epk".equals(name)) {
                    // do whatever you need to set apu from the epk
                    // this just shows that you can access it
                    Key key = jwk.getKey();
                    byte[] encoded = key.getEncoded();
                    String value = Base64Url.encode(encoded);
                    setStringHeaderValue("apu", value);
                }
            }
        }
    

  4. Brian Campbell

    “Thanks a lot” can come across with different connotations but I’m assuming/hoping it’s positive here. Would you mind closing the issue, if you’ve got a path forward?

  5. Log in to comment