/// <summary> /// Decypts a JWE by performing necessary decompression/decryption and authenticated decryption as defined in RFC7516. /// </summary> /// <param name="jwe">JWE to decrypt.</param> /// <param name="key">key for decoding suitable for JWE algorithm used to encrypt the CEK.</param> /// <param name="expectedJweAlg">The algorithm type that we expect to receive in the header.</param> /// <param name="expectedJweEnc">The encryption type that we expect to receive in the header.</param> /// <param name="settings">optional settings to override global DefaultSettings</param> /// <returns>Decrypted plaintext as binary data</returns> /// <exception cref="IntegrityException">if AEAD operation validation failed</exception> /// <exception cref="EncryptionException">if JWE can't be decrypted</exception> /// <exception cref="InvalidAlgorithmException">if encryption or compression algorithm is not supported</exception> public static (byte[] Plaintext, IDictionary <string, object> JoseHeaders, byte[] Aad) Decrypt(string jwe, object key, JweAlgorithm?expectedJweAlg = null, JweEncryption?expectedJweEnc = null, JwtSettings settings = null) { Ensure.IsNotEmpty(jwe, "Incoming jwe expected to be in a valid serialization form, not empty, whitespace or null."); settings = GetSettings(settings); ParsedJwe parsedJwe = ParsedJwe.Parse(jwe, settings); IDictionary <string, object> protectedHeader = settings.JsonMapper.Parse <Dictionary <string, object> >( Encoding.UTF8.GetString(parsedJwe.ProtectedHeaderBytes)); if (protectedHeader == null && parsedJwe.Encoding == SerializationMode.Compact) { throw new JoseException(string.Format("Protected header was missing but required with compact encoding.")); } IJweAlgorithm enc = null; /* * if (protectedHeader != null) * { * JweEncryption headerEnc = settings.JweAlgorithmFromHeader((string)protectedHeader["enc"]); * enc = settings.Jwe(headerEnc); * * if (enc == null && parsedJwe.Encoding == SerializationMode.Compact) * { * throw new JoseException(string.Format("Unsupported JWE algorithm requested: {0}", headerEnc)); * } * * if (expectedJweEnc != null && expectedJweEnc != headerEnc && parsedJwe.Encoding == SerializationMode.Compact) * { * throw new InvalidAlgorithmException("The encryption type passed to the Decrypt method did not match the encryption type in the header."); * } * } */ var algMatchingRecipients = parsedJwe.Recipients.Select(r => { var joseHeader = Dictionaries.MergeHeaders(protectedHeader, parsedJwe.UnprotectedHeader, r.Header); return(new { JoseHeader = joseHeader, HeaderAlg = settings.JwaAlgorithmFromHeader((string)joseHeader["alg"]), EncryptedCek = r.EncryptedCek, }); }) .Where(r => (expectedJweAlg == null || expectedJweAlg == r.HeaderAlg)); if (!algMatchingRecipients.Any()) { throw new InvalidAlgorithmException("The algorithm type passed to the Decrypt method did not match the algorithm type in the header."); } var exceptions = new List <Exception>(); foreach (var recipient in algMatchingRecipients) { IKeyManagement keys = settings.Jwa(recipient.HeaderAlg); if (keys == null) { throw new JoseException(string.Format("Unsupported JWA algorithm requested: {0}", recipient.HeaderAlg)); } try { JweEncryption headerEnc = settings.JweAlgorithmFromHeader((string)recipient.JoseHeader["enc"]); enc = settings.Jwe(headerEnc); if (enc == null) { throw new JoseException(string.Format("Unsupported JWE algorithm requested: {0}", headerEnc)); } if (expectedJweEnc != null && expectedJweEnc != headerEnc) { throw new InvalidAlgorithmException("The encryption type passed to the Decrypt method did not match the encryption type in the header."); } byte[] cek = keys.Unwrap(recipient.EncryptedCek, key, enc.KeySize, recipient.JoseHeader); byte[] asciiEncodedProtectedHeader = Encoding.ASCII.GetBytes(Base64Url.Encode(parsedJwe.ProtectedHeaderBytes)); byte[] aad = parsedJwe.Aad == null? Encoding.ASCII.GetBytes(Base64Url.Encode(parsedJwe.ProtectedHeaderBytes)) : Encoding.ASCII.GetBytes(string.Concat(Base64Url.Encode(parsedJwe.ProtectedHeaderBytes), ".", Base64Url.Encode(parsedJwe.Aad))); byte[] plaintext = enc.Decrypt(aad, cek, parsedJwe.Iv, parsedJwe.Ciphertext, parsedJwe.AuthTag); if (recipient.JoseHeader.TryGetValue("zip", out var compressionAlg)) { var compression = settings.Compression((string)compressionAlg); plaintext = compression.Decompress(plaintext); } return(Plaintext : plaintext, JoseHeaders : recipient.JoseHeader, Aad : parsedJwe.Aad); } catch (ArgumentException ex) { exceptions.Add(ex); } catch (JoseException ex) { exceptions.Add(ex); } } if (exceptions.Select(e => (e.GetType(), e.Message)).Distinct().Count() == 1) { // throw the first throw exceptions[0]; }