/// <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); }
/// <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)); } }