/// <summary> /// Asynchronously decrypts the fields, specified by JSONPath, that are contained in the given json document string. /// </summary> /// <param name="crypto"> /// The instance of <see cref="ICrypto"/> that ultimately responsible for performing decryption operations /// on field values. /// </param> /// <param name="jsonString">A string containing an json document.</param> /// <param name="jsonPathsToDecrypt">One or more JSONPaths of the fields to decrypt.</param> /// <param name="credentialName"> /// The name of the credential to use for this encryption operation, /// or null to use the default credential. /// </param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task that will contain the same json document, except with the specified fields decrypted.</returns> public static async Task <string> DecryptJsonAsync(this ICrypto crypto, string jsonString, IEnumerable <string> jsonPathsToDecrypt, string credentialName = null, CancellationToken cancellationToken = default(CancellationToken)) { if (crypto == null) { throw new ArgumentNullException(nameof(crypto)); } if (jsonString == null) { throw new ArgumentNullException(nameof(jsonString)); } if (jsonPathsToDecrypt == null) { throw new ArgumentNullException(nameof(jsonPathsToDecrypt)); } var token = JToken.Parse(jsonString); var decryptor = new Lazy <IAsyncDecryptor>(() => crypto.AsAsync().GetAsyncDecryptor(credentialName)); var anyPaths = false; foreach (var jsonPath in jsonPathsToDecrypt) { if (jsonPath == null) { throw new ArgumentException($"{nameof(jsonPathsToDecrypt)} cannot have null items.", nameof(jsonPathsToDecrypt)); } anyPaths = true; foreach (var match in token.SelectTokens(jsonPath).ToArray()) { var decryptedToken = JToken.Parse(await decryptor.Value.DecryptAsync(match.Value <string>(), cancellationToken)); if (ReferenceEquals(token, match)) { token = decryptedToken; continue; } switch (match.Parent) { case JProperty property: property.Value = decryptedToken; break; case JArray array: array[array.IndexOf(match)] = decryptedToken; break; } } } if (!anyPaths) { throw new ArgumentException($"{nameof(jsonPathsToDecrypt)} must have at least one item.", nameof(jsonPathsToDecrypt)); } return(token.ToString(Formatting.None)); }
/// <summary> /// Asynchronously decrypts the fields, specified by XPath, that are contained in the given xml document string. /// </summary> /// <param name="crypto"> /// The instance of <see cref="ICrypto"/> that ultimately responsible for performing decryption operations /// on field values. /// </param> /// <param name="xmlString">A string containing an xml document.</param> /// <param name="xpathsToDecrypt">One or more XPaths of the fields to decrypt.</param> /// <param name="credentialName"> /// The name of the credential to use for this encryption operation, /// or null to use the default credential. /// </param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task that will contain the same xml document, except with the specified fields decrypted.</returns> public static async Task <string> DecryptXmlAsync(this ICrypto crypto, string xmlString, IEnumerable <string> xpathsToDecrypt, string credentialName = null, CancellationToken cancellationToken = default(CancellationToken)) { if (crypto == null) { throw new ArgumentNullException(nameof(crypto)); } if (xmlString == null) { throw new ArgumentNullException(nameof(xmlString)); } if (xpathsToDecrypt == null) { throw new ArgumentNullException(nameof(xpathsToDecrypt)); } var doc = new XmlDocument(); doc.LoadXml(xmlString); var navigator = doc.CreateNavigator(); var decryptor = new Lazy <IAsyncDecryptor>(() => crypto.AsAsync().GetAsyncDecryptor(credentialName)); var anyPaths = false; foreach (var xpath in xpathsToDecrypt) { if (xpath == null) { throw new ArgumentException($"{nameof(xpathsToDecrypt)} cannot have null items.", nameof(xpathsToDecrypt)); } anyPaths = true; foreach (XPathNavigator match in navigator.Select(xpath)) { var decrypted = await decryptor.Value.DecryptAsync(match.InnerXml, cancellationToken).ConfigureAwait(false); if (decrypted != match.InnerXml) { try { match.InnerXml = decrypted; } catch { match.SetValue(decrypted); } } } } if (!anyPaths) { throw new ArgumentException($"{nameof(xpathsToDecrypt)} must have at least one item.", nameof(xpathsToDecrypt)); } return(doc.OuterXml); }
/// <summary> /// Asynchronously encrypts the fields, specified by XPath, that are contained in the given xml document string. /// </summary> /// <param name="crypto"> /// The instance of <see cref="ICrypto"/> that ultimately responsible for performing encryption operations /// on field values. /// </param> /// <param name="xmlString">A string containing an xml document.</param> /// <param name="xpathsToEncrypt">One or more XPaths of the fields to encrypt.</param> /// <param name="credentialName"> /// The name of the credential to use for this encryption operation, /// or null to use the default credential. /// </param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A task that will contain the same xml document, except with the specified fields encrypted.</returns> public static async Task <string> EncryptXmlAsync(this ICrypto crypto, string xmlString, IEnumerable <string> xpathsToEncrypt, string credentialName = null, CancellationToken cancellationToken = default(CancellationToken)) { if (crypto == null) { throw new ArgumentNullException(nameof(crypto)); } if (xmlString == null) { throw new ArgumentNullException(nameof(xmlString)); } if (xpathsToEncrypt == null) { throw new ArgumentNullException(nameof(xpathsToEncrypt)); } var doc = new XmlDocument(); doc.LoadXml(xmlString); var navigator = doc.CreateNavigator(); var encryptor = new Lazy <IAsyncEncryptor>(() => crypto.AsAsync().GetAsyncEncryptor(credentialName)); var anyPaths = false; foreach (var xpath in xpathsToEncrypt) { if (xpath == null) { throw new ArgumentException($"{nameof(xpathsToEncrypt)} cannot have null items.", nameof(xpathsToEncrypt)); } anyPaths = true; foreach (XPathNavigator match in navigator.Select(xpath)) { // If there are any child elements, or the value contains escaped characters... if (match.HasChildren && match.Value != match.InnerXml) { // ...encrypt the InnerXml property. var plaintext = match.InnerXml; // SetValue throws if the navigator has any child elements. while (match.MoveToFirstChild()) { match.DeleteSelf(); } match.SetValue(await encryptor.Value.EncryptAsync(plaintext, cancellationToken).ConfigureAwait(false)); } else { // Else, this is a plain value, so encrypt the Value property. match.SetValue(await encryptor.Value.EncryptAsync(match.Value, cancellationToken).ConfigureAwait(false)); } } } if (!anyPaths) { throw new ArgumentException($"{nameof(xpathsToEncrypt)} must have at least one item.", nameof(xpathsToEncrypt)); } return(doc.OuterXml); }