azure-encryption-extensions
azure-encryption-extensions copied to clipboard
Issue in using MemoryStream - Upload/Download the Azure Blob file using AesCryptoServiceProvider Encrypt and Decrypt algorithm.
We have encrypting file and storing in the Azure blob using AesCryptoServiceProvider, no issues in storing the file in the Azure blob.
When we try to decrypt and download the file using MemomryStream, file is not downloading from the blob.
Always, MemoryStream will be empty. Please let us know if any solution to decrypt and download Azure Blob using MemoryStream
Below code is being used.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["TestConnectionString"]);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("TestStorage");
container.CreateIfNotExists();
CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);
using (Stream blobStream = blockBlob.OpenRead(AccessCondition.GenerateEmptyCondition(), null, null))
{
Stream st = DecryptAzureBlob(blobStream, KeyIdentifier);
using (var memoryStream = new MemoryStream())
{
fileStream.Write(memoryStream);
}
Response.Clear();
Response.ContentType = blockBlob.Properties.ContentType;
Response.AddHeader("content-disposition", "attachment; filename=" + blobName.ToString());
Response.BinaryWrite(memoryStream.ToArray());
Response.End();
}
Decryption Code
Public Stream DecryptAzureBlob(Stream blobStream, string KeyIdentifier)
{
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
byte[] ivBytes = new byte[aesAlg.BlockSize / 8];
streamToDecrypt.Read(ivBytes, 0, ivBytes.Length);
aesAlg.Key = GetEncryptedKey(KeyIdentifier); // getting the key from Azure Blob
aesAlg.IV = ivBytes;
ICryptoTransform decryptor = aesAlg.CreateDecryptor();
CryptoStream cryptoStream = new CryptoStream(streamToDecrypt, decryptor, CryptoStreamMode.Read);
return cryptoStream;
}
}
Ok did you try using the built in stream download? It isn't demonstrated in the sample but it should do this for you. Something more similar to:
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("TestStorage");
container.CreateIfNotExists();
CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);
var provider = new AsymmetricBlobCryptoProvider([provide key]);
using (var memoryStream = new MemoryStream())
{
blockBlob.DownloadToStreamEncrypted(provider, memoryStream);
Response.Clear();
Response.ContentType = blockBlob.Properties.ContentType;
Response.AddHeader("content-disposition", "attachment; filename=" + blobName.ToString());
Response.BinaryWrite(memoryStream.ToArray());
Response.End();
}
You will have to instantiate that provider correctly, presumably with your GetEncryptedKey(...) call, but I think this should work, or at least get closer.
Also reference a unit test which uploads and downloads as memory streams. It uses a different overload of the provider than you may use but is very similar. https://github.com/stefangordon/azure-encryption-extensions/blob/master/AzureEncryptionExtensionsTests/FunctionalTests.cs#L194
On a related note you may consider looking at this sample, it does not use the encryption extensions but can easily be modified to use them. Note that it returns the stream directly so that the entire stream never has to be buffer on memory or disk (which is good for large files).
http://arcware.net/upload-and-download-files-with-web-api-and-azure-blob-storage/
I know another user modified this sample with Encryption Extensions successfully. The only non-obvious change required was that this sample sets the "Length" header during download to the length of the Azure Blob, which is of course incorrect for encrypted data, so you must remove that.
Specifically this download approach:
public async Task<HttpResponseMessage> GetBlobDownload(int blobId)
{
// IMPORTANT: This must return HttpResponseMessage instead of IHttpActionResult
try
{
// replace this part with our async stream download
var result = await _service.DownloadBlob(blobId);
if (result == null)
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
// Reset the stream position; otherwise, download will not work
result.BlobStream.Position = 0;
// Create response message with blob stream as its content
var message = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(result.BlobStream)
};
// Set content headers
// remove this line probably
message.Content.Headers.ContentLength = result.BlobLength;
message.Content.Headers.ContentType = new MediaTypeHeaderValue(result.BlobContentType);
message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = HttpUtility.UrlDecode(result.BlobFileName),
Size = result.BlobLength
};
return message;
}
catch (Exception ex)
{
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
Content = new StringContent(ex.Message)
};
}
}
Thanks Stefan. Download is working after setting Response.AddHeader("Content-Length", downloadedStream.Length.ToString());
We will check your other suggestions on larger files.
There is no luck to achieve download of large files using MemoryStream. We have to decrypt the files of maximum 2 GB.