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); }
private async Task <Stream> DeserializeAndDecryptResponseAsync( Stream content, CosmosDiagnosticsContext diagnosticsContext, CancellationToken cancellationToken) { JObject contentJObj = EncryptionProcessor.BaseSerializer.FromStream <JObject>(content); JArray result = new JArray(); if (!(contentJObj.SelectToken(Constants.DocumentsResourcePropertyName) is JArray documents)) { throw new InvalidOperationException("Feed response Body Contract was violated. Feed response did not have an array of Documents"); } foreach (JToken value in documents) { if (!(value is JObject document)) { result.Add(value); continue; } try { JObject decryptedDocument; if (this.pathsToEncrypt != null) { decryptedDocument = await PropertyEncryptionProcessor.DecryptAsync( document, this.encryptor, diagnosticsContext, this.pathsToEncrypt, cancellationToken); } else { decryptedDocument = await EncryptionProcessor.DecryptAsync( document, this.encryptor, diagnosticsContext, cancellationToken); } result.Add(decryptedDocument); } catch (Exception exception) { if (this.decryptionResultHandler == null) { throw; } result.Add(document); MemoryStream memoryStream = EncryptionProcessor.BaseSerializer.ToStream(document); Debug.Assert(memoryStream != null); bool wasBufferReturned = memoryStream.TryGetBuffer(out ArraySegment <byte> encryptedStream); Debug.Assert(wasBufferReturned); this.decryptionResultHandler( DecryptionResult.CreateFailure( encryptedStream, exception)); } } JObject decryptedResponse = new JObject(); foreach (JProperty property in contentJObj.Properties()) { if (property.Name.Equals(Constants.DocumentsResourcePropertyName)) { decryptedResponse.Add(property.Name, (JToken)result); } else { decryptedResponse.Add(property.Name, property.Value); } } return(EncryptionProcessor.BaseSerializer.ToStream(decryptedResponse)); }
/// <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)); }