Exemplo n.º 1
0
        /// <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];
            }