Handle Convert.ToBase64String out of memory exception when converting to string - c#

When I try to do the following with .zip folder witch contains some videos I get out of memory exeption.
Byte[] bytes = File.ReadAllBytes(#"C:\folderWithVideos.zip");
String base64File= Convert.ToBase64String(bytes);//<----- out of memory exception
How to handle this exception properly? I mean without try-catch, I have tried something like:
String base64File;
if (bytes.Length <= System.Int32.MaxValue)
base64File = Convert.ToBase64String(bytes);
But it didn't helped, but bytes.Length <= 255 did helped, but I'm not sure that 255 is the right number.

Based on the code shown in the blog the following code works.
// using System.Security.Cryptography
private void ConvertLargeFile()
{
//encode
var filein = #"C:\Users\test\Desktop\my.zip";
var fileout = #"C:\Users\test\Desktop\Base64Zip";
using (FileStream fs = File.Open(fileout, FileMode.Create))
using (var cs = new CryptoStream(fs, new ToBase64Transform(),
CryptoStreamMode.Write))
using (var fi = File.Open(filein, FileMode.Open))
{
fi.CopyTo(cs);
}
// the zip file is now stored in base64zip
// and decode
using (FileStream f64 = File.Open(fileout, FileMode.Open))
using (var cs = new CryptoStream(f64, new FromBase64Transform(),
CryptoStreamMode.Read))
using (var fo = File.Open(filein + ".orig", FileMode.Create))
{
cs.CopyTo(fo);
}
// the original file is in my.zip.orig
// use the commandlinetool
// fc my.zip my.zip.orig
// to verify that the start file and the encoded and decoded file
// are the same
}
he code uses standard classes found in System.Security.Cryptography namespace and uses a CryptoStream and the FromBase64Transform and its counterpart ToBase64Transform

Related

Store and retrieve AES key to file

how do you store AES key to file and then retrieve it?
I have tried saving it as string to a text file then retrieve it back and convert it to byte, but the decryption never works…
using (var provider = new AesCryptoServiceProvider())
{
var cryptoTransform = provider.CreateEncryptor();
Key = provider.Key;
IV = provider.IV;
string key_string = Encoding.ASCII.GetString(Key);
File.WriteAllText(#"C:\Documents\AES\Key.txt", key_string);
string string_key = System.IO.File.ReadAllText(#"C:\Documents\AES\Key.txt");
testKey_bytes = Encoding.ASCII.GetBytes(string_key); ;
As mentioned before you can save it using File.WriteAllBytes() and read it back using File.ReadAllBytes().
But you also have to store the IV (at least in CBC mode). This can be done via the BinaryReader and BinaryWriter. Please be assured that your key will be stored in a safe place:
private static void SaveIVAndKey(AesCryptoServiceProvider provider)
{
using (FileStream fileStream = new FileStream("YourKeyFile.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
using (BinaryWriter binaryWriter = new BinaryWriter(fileStream))
{
binaryWriter.Write(provider.IV.Length);
binaryWriter.Write(provider.IV);
binaryWriter.Write(provider.Key.Length);
binaryWriter.Write(provider.Key);
}
}
}
To read it back in, you can use the following method:
private static void LoadIVAndKey(AesCryptoServiceProvider provider)
{
using (FileStream fileStream = new FileStream("YourKeyFile.txt", FileMode.Open, FileAccess.Read))
{
using (BinaryReader binaryReader = new BinaryReader(fileStream))
{
provider.IV = binaryReader.ReadBytes(binaryReader.ReadInt32());
provider.Key = binaryReader.ReadBytes(binaryReader.ReadInt32());
}
}
}
I've managed to work it out:)
File.WriteAllBytes(#"C:\Documents\AES\Key.txt", Key.ToArray());
testKey_bytes = File.ReadAllBytes(#"C:\Documents\AES\Key.txt");

Create zip file in memory from bytes (text with arbitrary encoding)

The application i'm developing needs to compress xml files into zip files and send them through http requests to a web service. As I dont need to keep the zip files, i'm just performing the compression in memory. The web service is denying my requests because the zip files are apparently malformed.
I know there is a solution in this question which works perfectly, but it uses a StreamWriter. My problem with that solution is that StreamWriter requires an encoding or assumes UTF-8, and I do not need to know the enconding of the xml files. I just need to read the bytes from those files, and store them inside a zip file, whatever encoding they use.
So, to be clear, this question has nothing to do with encodings, as I don't need to transform the bytes into text or the oposite. I just need to compress a byte[].
I'm using the next code to test how my zip file is malformed:
static void Main(string[] args)
{
Encoding encoding = Encoding.GetEncoding("ISO-8859-1");
string xmlDeclaration = "<?xml version=\"1.0\" encoding=\"" + encoding.WebName.ToUpperInvariant() + "\"?>";
string xmlBody = "<Test>ª!\"·$%/()=?¿\\|##~€¬'¡º</Test>";
string xmlContent = xmlDeclaration + xmlBody;
byte[] bytes = encoding.GetBytes(xmlContent);
string fileName = "test.xml";
string zipPath = #"C:\Users\dgarcia\test.zip";
Test(bytes, fileName, zipPath);
}
static void Test(byte[] bytes, string fileName, string zipPath)
{
byte[] zipBytes;
using (var memoryStream = new MemoryStream())
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: false))
{
var zipEntry = zipArchive.CreateEntry(fileName);
using (Stream entryStream = zipEntry.Open())
{
entryStream.Write(bytes, 0, bytes.Length);
}
//Edit: as the accepted answer states, the problem is here, because i'm reading from the memoryStream before disposing the zipArchive.
zipBytes = memoryStream.ToArray();
}
using (var fileStream = new FileStream(zipPath, FileMode.OpenOrCreate))
{
fileStream.Write(zipBytes, 0, zipBytes.Length);
}
}
If I try to open that file, I get an "Unexpected end of file" error. So apparently, the web service is correctly reporting a malformed zip file. What I have tried so far:
Flushing the entryStream.
Closing the entryStream.
Both flushing and closing the entryStream.
Note that if I open the zipArchive directly from the fileStream the zip file is formed with no errors. However, the fileStream is just there as a test, and I need to create my zip file in memory.
You are trying to get bytes from MemoryStream too early, ZipArchive did not write them all yet. Instead, do like this:
using (var memoryStream = new MemoryStream()) {
// note "leaveOpen" true, to not dispose memoryStream too early
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, leaveOpen: true)) {
var zipEntry = zipArchive.CreateEntry(fileName);
using (Stream entryStream = zipEntry.Open()) {
entryStream.Write(bytes, 0, bytes.Length);
}
}
// now, after zipArchive is disposed - all is written to memory stream
zipBytes = memoryStream.ToArray();
}
If you use a memory stream to load your text you can control the encoding type and it works across a WCF service. This is the implementation i am using currently and it works on my WCF services
private byte[] Zip(string text)
{
var bytes = Encoding.UTF8.GetBytes(text);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(mso, CompressionMode.Compress))
{
CopyTo(msi, gs);
}
return mso.ToArray();
}
}
private string Unzip(byte[] bytes)
{
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gs = new GZipStream(msi, CompressionMode.Decompress))
{
CopyTo(gs, mso);
}
return Encoding.UTF8.GetString(mso.ToArray());
}
}

The stream is invalid or no corresponding signature was found

I am trying to use SevenZipSharp to compress and decompress a memory stream. Compression is working fine but decompression is not. I think SevenZipSharp is not able to figure the archive type from the stream.
SevenZipCompressor compress = new SevenZip.SevenZipCompressor();
compress.CompressionLevel = CompressionLevel.Normal;
compress.CompressionMethod = CompressionMethod.Lzma
using (MemoryStream memStream = new MemoryStream())
{
compress.CompressFiles(memStream, #"d:\Temp1\MyFile.bmp");
using (FileStream file = new FileStream(#"d:\arch.7z", FileMode.Create, System.IO.FileAccess.Write))
{
memStream.CopyTo(file);
}
}
//works till here, file is created
Console.Read();
using (FileStream file = new FileStream(#"d:\arch.7z", FileMode.Open, System.IO.FileAccess.Read))
{
using (MemoryStream memStream = new MemoryStream())
{
file.CopyTo(memStream);
//throws exception here on this line
using (var extractor = new SevenZipExtractor(memStream))
{
extractor.ExtractFiles(#"d:\x", 0);
}
}
}
Try to see if your output file can be loaded using the 7Zip client. I'm guessing that it will fail.
The problem lies in the writing to the memorystream. Say, you write 100 bytes to the stream, it will be on position 100. When you use CopyTo, the stream will be copied from the current position, not the start of the stream.
So you'll have to reset the position to 0 after reading/writing to allow the next reader to read all the data. For instance when creating the 7Zip file:
using (MemoryStream memStream = new MemoryStream())
{
// Position starts at 0
compress.CompressFiles(memStream, #"d:\Temp1\MyFile.bmp");
// Position is now N
memStream.Position = 0; // <-- Reset the position to 0.
using (FileStream file = new FileStream(#"d:\arch.7z", FileMode.Create, System.IO.FileAccess.Write))
{
// Will copy all data in the stream from current position till the end of the stream.
memStream.CopyTo(file);
}
}

Save a zip file to memory and unzip file from stream and get content

I am currently working on integrating Amazon Prime on our system and being stuck at getting the label back as ZPL format.
Basically, Amazon returns a base64 string, we will need to convert that string to a byte array, then save that array as a *.gzip file. From that gzip file, we can extract the content and get the zpl label content.
My question is, how we can do all of above without storing any temp files to system. I have researched some solutions but none is working for me.
My current code as below:
var str = "base64string";
var label = Convert.FromBase64String(str);
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var demoFile = archive.CreateEntry("label.zip");
var entryStream = demoFile.Open();
using (var bw = new BinaryWriter(entryStream))
{
bw.Write(label);
}
var data = new MemoryStream();
using (var zip = ZipFile.Read(entryStream))
{
zip["label"].Extract(data);
}
data.Seek(0, SeekOrigin.Begin);
entryStream.Close();
}
using (var fileStream = new FileStream(#"D:\test.zip", FileMode.Create))
{
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(fileStream);
}
}
If I save the file as test.zip, I can successfully get the label back. But if I try to extract it directly to another stream, I get an error
A stream from ZipArchiveEntry has been disposed
I've done something similar, taking PNG label data from a zipped web response. This is how I went about that
using (WebClient webClient = new WebClient())
{
// Download. Expect this to be a zip file
byte[] data = webClient.DownloadData(urlString);
MemoryStream memoryStream = new MemoryStream(data);
ZipArchive zipArchive = new ZipArchive(memoryStream);
foreach (var zipEntry in zipArchive.Entries)
{
// Can check file name here and ignore anything in zip we're not expecting
if (!zipEntry.Name.EndsWith(".png")) continue;
// Open zip entry as stream
Stream extractedFile = zipEntry.Open();
// Convert stream to memory stream
MemoryStream extractedMemoryStream = new MemoryStream();
extractedFile.CopyTo(extractedMemoryStream);
// At this point the extractedMemoryStream is a sequence of bytes containing image data.
// In this test project I'm pushing that into a bitmap image, just to see something on screen, but could as easily be written to a file or passed for storage to sql or whatever.
BitmapDecoder decoder = PngBitmapDecoder.Create(extractedMemoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapFrame frame = decoder.Frames.First();
frame.Freeze();
this.LabelImage.Source = frame;
}
}
I was overthinking it. I finally found a simple way to do it. We just need to convert that base64 string to bytes array and use GzipStream to directly decompress it. I leave the solution here in case someone needs it. Thanks!
var label = Convert.FromBase64String(str);
using (var compressedStream = new MemoryStream(label))
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}

How do I do a SHA1 File Checksum in C#?

How do I use the SHA1CryptoServiceProvider() on a file to create a SHA1 Checksum of the file?
using (FileStream fs = new FileStream(#"C:\file\location", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
{
using (SHA1Managed sha1 = new SHA1Managed())
{
byte[] hash = sha1.ComputeHash(bs);
StringBuilder formatted = new StringBuilder(2 * hash.Length);
foreach (byte b in hash)
{
formatted.AppendFormat("{0:X2}", b);
}
}
}
formatted contains the string representation of the SHA-1 hash. Also, by using a FileStream instead of a byte buffer, ComputeHash computes the hash in chunks, so you don't have to load the entire file in one go, which is helpful for large files.
With the ComputeHash method. See here:
ComputeHash
Example snippet:
using(var cryptoProvider = new SHA1CryptoServiceProvider())
{
string hash = BitConverter
.ToString(cryptoProvider.ComputeHash(buffer));
//do something with hash
}
Where buffer is the contents of your file.
If you are already reading the file as a stream, then the following technique calculates the hash as you read it. The only caveat is that you need to consume the whole stream.
class Program
{
static void Main(string[] args)
{
String sourceFileName = "C:\\test.txt";
Byte[] shaHash;
//Use Sha1Managed if you really want sha1
using (var shaForStream = new SHA256Managed())
using (Stream sourceFileStream = File.Open(sourceFileName, FileMode.Open))
using (Stream sourceStream = new CryptoStream(sourceFileStream, shaForStream, CryptoStreamMode.Read))
{
//Do something with the sourceStream
//NOTE You need to read all the bytes, otherwise you'll get an exception ({"Hash must be finalized before the hash value is retrieved."})
while(sourceStream.ReadByte() != -1);
shaHash = shaForStream.Hash;
}
Console.WriteLine(Convert.ToBase64String(shaHash));
}
}
Also you can try:
FileStream fop = File.OpenRead(#"C:\test.bin");
string chksum = BitConverter.ToString(System.Security.Cryptography.SHA1.Create().ComputeHash(fop));

Categories