private static async Task <JObject> DecryptContentAsync( EncryptionProperties encryptionProperties, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { if (encryptionProperties.EncryptionFormatVersion != 2) { throw new NotSupportedException($"Unknown encryption format version: {encryptionProperties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version."); } byte[] plainText = await encryptor.DecryptAsync( encryptionProperties.EncryptedData, encryptionProperties.DataEncryptionKeyId, encryptionProperties.EncryptionAlgorithm, cancellationToken); if (plainText == null) { throw new InvalidOperationException($"{nameof(Encryptor)} returned null plainText from {nameof(DecryptAsync)}."); } JObject plainTextJObj; using (MemoryStream memoryStream = new MemoryStream(plainText)) using (StreamReader streamReader = new StreamReader(memoryStream)) using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader)) { plainTextJObj = JObject.Load(jsonTextReader); } return(plainTextJObj); }
public static async Task <JObject> DecryptAsync( JObject document, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(document != null); Debug.Assert(encryptor != null); Debug.Assert(diagnosticsContext != null); if (!document.TryGetValue(Constants.EncryptedInfo, out JToken encryptedInfo)) { return(document); } EncryptionProperties encryptionProperties = JsonConvert.DeserializeObject <EncryptionProperties>(encryptedInfo.ToString()); JObject plainTextJObj = await EncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); document.Remove(Constants.EncryptedInfo); foreach (JProperty property in plainTextJObj.Properties()) { document.Add(property.Name, property.Value); } return(document); }
public static async Task <JObject> DecryptAsync( JObject document, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, IReadOnlyDictionary <List <string>, string> pathsToEncrypt, CancellationToken cancellationToken) { Debug.Assert(document != null); Debug.Assert(encryptor != null); Debug.Assert(diagnosticsContext != null); foreach (List <string> paths in pathsToEncrypt.Keys) { foreach (string path in paths) { if (document.TryGetValue(path.Substring(1), out JToken propertyValue)) { EncryptionProperties encryptionProperties = new EncryptionProperties( encryptionFormatVersion: 2, CosmosEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, pathsToEncrypt[paths], propertyValue.ToObject <byte[]>(), path); JObject propPlainTextJObj = await PropertyEncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); foreach (JProperty property in propPlainTextJObj.Properties()) { document[property.Name] = property.Value; } } } } if (document.TryGetValue(Constants.EncryptedInfo, out JToken encryptedInfo)) { EncryptionProperties encryptionProperties = JsonConvert.DeserializeObject <EncryptionProperties>(encryptedInfo.ToString()); JObject plainTextJObj = await PropertyEncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); document.Remove(Constants.EncryptedInfo); foreach (JProperty property in plainTextJObj.Properties()) { document.Add(property.Name, property.Value); } } return(document); }
/// <remarks> /// If there isn't any data that needs to be decrypted, input stream will be returned without any modification. /// Else input stream will be disposed, and a new stream is returned. /// In case of an exception, input stream won't be disposed, but position will be end of stream. /// </remarks> public static async Task <Stream> DecryptAsync( Stream input, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(input != null); Debug.Assert(input.CanSeek); Debug.Assert(encryptor != null); Debug.Assert(diagnosticsContext != null); JObject itemJObj; using (StreamReader sr = new StreamReader(input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { using (JsonTextReader jsonTextReader = new JsonTextReader(sr)) { itemJObj = JsonSerializer.Create().Deserialize <JObject>(jsonTextReader); } } JProperty encryptionPropertiesJProp = itemJObj.Property(Constants.EncryptedInfo); JObject encryptionPropertiesJObj = null; if (encryptionPropertiesJProp != null && encryptionPropertiesJProp.Value != null && encryptionPropertiesJProp.Value.Type == JTokenType.Object) { encryptionPropertiesJObj = (JObject)encryptionPropertiesJProp.Value; } if (encryptionPropertiesJObj == null) { input.Position = 0; return(input); } EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject <EncryptionProperties>(); JObject plainTextJObj = await EncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); foreach (JProperty property in plainTextJObj.Properties()) { itemJObj.Add(property.Name, property.Value); } itemJObj.Remove(Constants.EncryptedInfo); input.Dispose(); return(EncryptionProcessor.baseSerializer.ToStream(itemJObj)); }
public static async Task <(JObject, DecryptionContext)> DecryptAsync( JObject document, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(document != null); Debug.Assert(encryptor != null); Debug.Assert(diagnosticsContext != null); if (!document.TryGetValue(Constants.EncryptedInfo, out JToken encryptedInfo)) { return(document, null); } EncryptionProperties encryptionProperties = JsonConvert.DeserializeObject <EncryptionProperties>(encryptedInfo.ToString()); JObject plainTextJObj = await EncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); document.Remove(Constants.EncryptedInfo); List <string> pathsDecrypted = new List <string>(); foreach (JProperty property in plainTextJObj.Properties()) { document.Add(property.Name, property.Value); pathsDecrypted.Add("/" + property.Name); } DecryptionInfo decryptionInfo = new DecryptionInfo( pathsDecrypted, encryptionProperties.DataEncryptionKeyId); DecryptionContext decryptionContext = new DecryptionContext( new List <DecryptionInfo>() { decryptionInfo }); return(document, decryptionContext); }
/// <remarks> /// If there isn't any PathsToEncrypt, input stream will be returned without any modification. /// Else input stream will be disposed, and a new stream is returned. /// In case of an exception, input stream won't be disposed, but position will be end of stream. /// </remarks> public static async Task <Stream> EncryptAsync( Stream input, Encryptor encryptor, EncryptionOptions encryptionOptions, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { Debug.Assert(diagnosticsContext != null); if (input == null) { throw new ArgumentNullException(nameof(input)); } if (encryptor == null) { throw new ArgumentNullException(nameof(encryptor)); } if (encryptionOptions == null) { throw new ArgumentNullException(nameof(encryptionOptions)); } if (string.IsNullOrWhiteSpace(encryptionOptions.DataEncryptionKeyId)) { throw new ArgumentNullException(nameof(encryptionOptions.DataEncryptionKeyId)); } if (string.IsNullOrWhiteSpace(encryptionOptions.EncryptionAlgorithm)) { throw new ArgumentNullException(nameof(encryptionOptions.EncryptionAlgorithm)); } if (encryptionOptions.PathsToEncrypt == null) { throw new ArgumentNullException(nameof(encryptionOptions.PathsToEncrypt)); } if (!encryptionOptions.PathsToEncrypt.Any()) { return(input); } foreach (string path in encryptionOptions.PathsToEncrypt) { if (string.IsNullOrWhiteSpace(path) || path[0] != '/' || path.LastIndexOf('/') != 0) { throw new ArgumentException($"Invalid path {path ?? string.Empty}", nameof(encryptionOptions.PathsToEncrypt)); } } JObject itemJObj = EncryptionProcessor.baseSerializer.FromStream <JObject>(input); JObject toEncryptJObj = new JObject(); foreach (string pathToEncrypt in encryptionOptions.PathsToEncrypt) { string propertyName = pathToEncrypt.Substring(1); if (!itemJObj.TryGetValue(propertyName, out JToken propertyValue)) { throw new ArgumentException($"{nameof(encryptionOptions.PathsToEncrypt)} includes a path: '{pathToEncrypt}' which was not found."); } toEncryptJObj.Add(propertyName, propertyValue.Value <JToken>()); itemJObj.Remove(propertyName); } MemoryStream memoryStream = EncryptionProcessor.baseSerializer.ToStream <JObject>(toEncryptJObj); Debug.Assert(memoryStream != null); Debug.Assert(memoryStream.TryGetBuffer(out _)); byte[] plainText = memoryStream.ToArray(); byte[] cipherText = await encryptor.EncryptAsync( plainText, encryptionOptions.DataEncryptionKeyId, encryptionOptions.EncryptionAlgorithm, cancellationToken); if (cipherText == null) { throw new InvalidOperationException($"{nameof(Encryptor)} returned null cipherText from {nameof(EncryptAsync)}."); } EncryptionProperties encryptionProperties = new EncryptionProperties( encryptionFormatVersion: 2, encryptionOptions.EncryptionAlgorithm, encryptionOptions.DataEncryptionKeyId, encryptedData: cipherText); itemJObj.Add(Constants.EncryptedInfo, JObject.FromObject(encryptionProperties)); input.Dispose(); return(EncryptionProcessor.baseSerializer.ToStream(itemJObj)); }
/// <remarks> /// If there isn't any data that needs to be decrypted, input stream will be returned without any modification. /// Else input stream will be disposed, and a new stream is returned. /// In case of an exception, input stream won't be disposed, but position will be end of stream. /// </remarks> public static async Task <Stream> DecryptAsync( Stream input, Encryptor encryptor, CosmosDiagnosticsContext diagnosticsContext, IReadOnlyDictionary <List <string>, string> pathsToEncrypt, CancellationToken cancellationToken) { Debug.Assert(input != null); Debug.Assert(input.CanSeek); Debug.Assert(encryptor != null); Debug.Assert(diagnosticsContext != null); JObject itemJObj; using (StreamReader sr = new StreamReader(input, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { using JsonTextReader jsonTextReader = new JsonTextReader(sr); itemJObj = JsonSerializer.Create().Deserialize <JObject>(jsonTextReader); } if (pathsToEncrypt != null) { foreach (List <string> paths in pathsToEncrypt.Keys) { foreach (string path in paths) { if (itemJObj.TryGetValue(path.Substring(1), out JToken propertyValue)) { EncryptionProperties encryptionProperties = new EncryptionProperties( encryptionFormatVersion: 2, CosmosEncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256, pathsToEncrypt[paths], propertyValue.ToObject <byte[]>(), path); JObject propPlainTextJObj = await PropertyEncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); foreach (JProperty property in propPlainTextJObj.Properties()) { itemJObj[property.Name] = property.Value; } } } input.Dispose(); } } JToken token = itemJObj[Constants.EncryptedInfo]; if (token != null) { JProperty encryptionPropertiesJProp = itemJObj.Property(Constants.EncryptedInfo); JObject encryptionPropertiesJObj = null; if (encryptionPropertiesJProp != null && encryptionPropertiesJProp.Value != null && encryptionPropertiesJProp.Value.Type == JTokenType.Object) { encryptionPropertiesJObj = (JObject)encryptionPropertiesJProp.Value; } if (encryptionPropertiesJObj == null) { input.Position = 0; return(input); } EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject <EncryptionProperties>(); JObject plainTextJObj = await PropertyEncryptionProcessor.DecryptContentAsync( encryptionProperties, encryptor, diagnosticsContext, cancellationToken); foreach (JProperty property in plainTextJObj.Properties()) { itemJObj.Add(property.Name, property.Value); } itemJObj.Remove(Constants.EncryptedInfo); input.Dispose(); } return(EncryptionProcessor.BaseSerializer.ToStream(itemJObj)); }