Example #1
0
        /// <summary>
        /// Parses JWE token, extracts and unmarshal protected+unprotcted+per recipient headers
        /// This method is NOT performing integrity checking and actual decryption
        /// </summary>
        /// <param name="jwe">Serialized JWE string to decrypt.</param>
        /// <param name="settings">optional settings to override global DefaultSettings</param>
        /// <returns>List of Jose headers. For Compact and Flattened this will be length 1 and contain just the protected header.
        ///  For General Json this will be the Jose headers (merge of protected, unprotected and per-recipient).</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 JweToken Headers(string jwe, JwtSettings settings = null)
        {
            settings = GetSettings(settings);

            var token = JweToken.FromString(jwe, settings.JsonMapper);

            var protectedHeader = settings.JsonMapper.Parse <IDictionary <string, object> >(
                Encoding.UTF8.GetString(token.ProtectedHeaderBytes));

            foreach (var recipient in token.Recipients)
            {
                try
                {
                    recipient.JoseHeader = Dictionaries.MergeHeaders(protectedHeader, token.UnprotectedHeader, recipient.Header);
                }
                catch (ArgumentException)
                {
                    throw new JoseException("Invalid JWE data, duplicate header keys found between protected, unprotected and recipient headers");
                }
            }

            return(token);
        }
Example #2
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 JweToken object</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 JweToken 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);

            JweToken token = Headers(jwe);

            if (token.ProtectedHeaderBytes == null && token.Encoding == SerializationMode.Compact)
            {
                throw new JoseException(string.Format("Protected header was missing but required with compact encoding."));
            }

            var exceptions = new List <Exception>();

            foreach (var recipient in token.Recipients)
            {
                var headerAlg    = settings.JwaAlgorithmFromHeader((string)recipient.JoseHeader["alg"]);
                var encryptedCek = recipient.EncryptedCek;

                // skip recipient if asked to do strict validation
                if (expectedJweAlg != null && expectedJweAlg != headerAlg)
                {
                    continue;
                }

                IKeyManagement keys = settings.Jwa(headerAlg);

                if (keys == null)
                {
                    throw new JoseException(string.Format("Unsupported JWA algorithm requested: {0}", headerAlg));
                }

                try
                {
                    JweEncryption headerEnc = settings.JweAlgorithmFromHeader((string)recipient.JoseHeader["enc"]);
                    IJweAlgorithm 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[] plaintext = enc.Decrypt(Aad(token.ProtectedHeaderBytes, token.Aad), cek, token.Iv, token.Ciphertext, token.AuthTag);

                    if (recipient.JoseHeader.TryGetValue("zip", out var compressionAlg))
                    {
                        var compression = settings.Compression((string)compressionAlg);

                        plaintext = compression.Decompress(plaintext);
                    }

                    token.PlaintextBytes = plaintext;
                    token.Recipient      = recipient;

                    return(token);
                }
                catch (ArgumentException ex)
                {
                    exceptions.Add(ex);
                }
                catch (JoseException ex)
                {
                    exceptions.Add(ex);
                }
            }

            // nobody was eligable for decryption
            if (exceptions.Count == 0)
            {
                throw new InvalidAlgorithmException("The algorithm type passed to the Decrypt method did not match the algorithm type in the header.");
            }

            // decryption failed
            if (exceptions.Select(e => new KeyValuePair <Type, String>(e.GetType(), e.Message)).Distinct().Count() == 1)
            {
                // throw the first
                throw exceptions[0];
            }
            else
            {
                throw new JoseException($"No recipients able to decrypt.", new AggregateException(exceptions));
            }
        }