Errors when using MultiEncrypter/MultiDecrypter with only one recipient

Issue #551 open
Theo Pack created an issue

Hi,

in our use case we use the library to encrypt JSON messages for one or more recipients and to decrypt them for recipients accordingly.

I noticed that if the MultiEncrypter is used with only one recipient, an invalid JWE is generated. The information about the encryption algorithm used is lost in the generated JWE.

The following example can be used to reproduce the bug:

ECKey jwk = new ECKeyGenerator(Curve.P_256)
        .keyUse(KeyUse.ENCRYPTION)
        .algorithm(JWEAlgorithm.ECDH_ES_A128KW)
        .generate();

JWEObjectJSON jwe = new JWEObjectJSON(
        new JWEHeader(EncryptionMethod.A128GCM),
        new Payload("Hello, world!"));

jwe.encrypt(new MultiEncrypter(new JWKSet(jwk)));
jwe.decrypt(new MultiDecrypter(jwk));

The generated JWE looks like:

{
  "ciphertext": "OUpEYZQnnXTFs7jVSQ",
  "protected": "eyJlbmMiOiJBMTI4R0NNIn0",   // decoded: {"enc":"A128GCM"}
  "recipients": [
    {
      "encrypted_key": "e2dzatT-zlIQNL5AkkI2Hx2nEwkZTJbB"
    }
  ],
  "tag": "9OWSTMmBHPLQYbB7wfWcSQ",
  "iv": "4VSnYkuEHxilo1P-"
}

This leads to the following error, when trying to decrypt the JWE:

Exception in thread "main" com.nimbusds.jose.JOSEException: The algorithm "alg" header parameter must not be null
    at com.nimbusds.jose.crypto.impl.JWEHeaderValidation.getAlgorithmAndEnsureNotNull(JWEHeaderValidation.java:50)
    at com.nimbusds.jose.crypto.MultiDecrypter.decrypt(MultiDecrypter.java:322)
    at com.nimbusds.jose.JWEObjectJSON.decrypt(JWEObjectJSON.java:556)
    at example.Main.main(Main.java:33)

I’m using the latest version 9.39.1 with openjdk-17.

I would also be willing to help with the solution, but I'm not sure where the right place would be. As I understand it, the JWEObjectJSON should ensure that the algorithm is drawn from either the protectedHeader or the unprotectedHeader during encryption.

During my first analyses, when I tried to understand the problem, I also noticed that the MultiDecrypter also has problems if there is only one recipient and the algorithm is stored in the unprotected header. As far as I understand the RFC 7516, this should be valid.

The following code will throw the same exception:

ECKey jwk = new ECKeyGenerator(Curve.P_256)
        .keyUse(KeyUse.ENCRYPTION)
        .algorithm(JWEAlgorithm.ECDH_ES_A128KW)
        .generate();

String jweString = """
        {
          "ciphertext": "9xhMUKE7p4q8om4XqQ",
          "protected": "eyJlbmMiOiJBMTI4R0NNIn0",
          "recipients": [
            {
              "encrypted_key": "cy4kUzylYaPdQ70p-zgyIRulcxFPNyqK",
              "header": {
                "epk": {
                  "kty": "EC",
                  "crv": "P-256",
                  "x": "M4IGUyaltmdmVEtJRU-fsWigsye4nVoIV8by7QVG5vw",
                  "y": "g_WhCLmDpy86cktOuIVFUAnvZiS-rioFZkYg3E8oSus"
                },
                "alg": "ECDH-ES+A128KW",
                "kid": "NNPVrleSyhpC1S99mM3YrY5WjyTpYYHHM6uHSMICrBw"
              }
            }
          ],
          "tag": "H8yHJrhVAJ5wxWsqaLtvKQ",
          "iv": "4YxtWzvhh5sZLTv1"
        }
        """;
JWEObjectJSON jwe = JWEObjectJSON.parse(jweString);
jwe.decrypt(new MultiDecrypter(jwk));

Comments (3)

  1. Yavor Vasilev
    • changed status to open

    Hello Theo,

    Thank you for reporting this issue.

    The Multi Encrypter / Decrypter functionality in the lib was an external contribution. We don't use at connect2id, but did spend some time after it was recevied to improve the quality so that it matches the standard of the library. It looks like a significant bug looking at your discovery.

  2. andy

    Hello,
    I believe this is because these two classes, along with JWEObjectJSON, attempt to place recipients into the encryptedKey variable during encryption and decryption,
    but they don't handle the case where there is only one recipient well.
    If you need some quick fixes, maybe override MultiEncrypter.encrypt() and just change the line 294
    from if (recipients.size() > 1) to if (!recipients.isEmpty()), then the encryption part should be fine.
    For the decryption part, maybe override JWEObjectJSON.getEncryptedKey(), and remove the part for only one recipient at lines 339 to 341,
    though this will make the method somewhat pointless, as it just encodes the recipients.

  3. Log in to comment