I am developing a process to compress and encrypt a byte array in my desktop application and send it via a WebMethod to my web application, then uncompress/unencrypt it back to a byte array. I am currently attempting to do this with SharpZipLib. The compression of the file seems to be working as expected. I am able to save the file to disk and extract it using 7zip without issue.
The problem I am having is when I receive the byte array on my web server and attempt to extract it.
I use the CompressData method to compress the data on the desktop side.
private byte[] CompressData(byte[] data, string password)
{
MemoryStream input = new MemoryStream(data);
MemoryStream ms = new MemoryStream();
ZipOutputStream os = new ZipOutputStream(ms);
os.SetLevel(9);
if (!string.IsNullOrEmpty(password)) os.Password = password;
ZipEntry entry = new ZipEntry("data")
{
DateTime = DateTime.Now
};
if (!string.IsNullOrEmpty(password)) entry.AESKeySize = 256;
os.PutNextEntry(entry);
StreamUtils.Copy(input, os, new byte[4096]);
os.CloseEntry();
os.IsStreamOwner = false;
os.Close();
ms.Position = 0;
return ms.ToArray();
}
I am using the following code to extract the data on the server end (taken almost verbatim from the SharpZipLib examples):
private byte[] DoRebuildData(byte[] data, string password)
{
MemoryStream inStream = new MemoryStream(data);
MemoryStream outputMemStream = new MemoryStream();
ZipOutputStream zipOut = new ZipOutputStream(outputMemStream)
{
IsStreamOwner = false // False stops the Close also Closing the underlying stream.
};
zipOut.SetLevel(3);
zipOut.Password = password; // optional
RecursiveExtractRebuild(inStream, zipOut);
inStream.Close();
// Must finish the ZipOutputStream to finalise output before using outputMemStream.
zipOut.Close();
outputMemStream.Position = 0;
return outputMemStream.ToArray();
}
// Calls itself recursively if embedded zip
//
private void RecursiveExtractRebuild(Stream str, ZipOutputStream os)
{
ZipFile zipFile = new ZipFile(str)
{
IsStreamOwner = false
};
foreach (ZipEntry zipEntry in zipFile)
{
if (!zipEntry.IsFile)
continue;
String entryFileName = zipEntry.Name; // or Path.GetFileName(zipEntry.Name) to omit folder
// Specify any other filtering here.
Stream zipStream = zipFile.GetInputStream(zipEntry);
// Zips-within-zips are extracted. If you don't want this and wish to keep embedded zips as-is, just delete these 3 lines.
if (entryFileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
RecursiveExtractRebuild(zipStream, os);
}
else
{
ZipEntry newEntry = new ZipEntry(entryFileName);
newEntry.DateTime = zipEntry.DateTime;
newEntry.Size = zipEntry.Size;
// Setting the Size will allow the zip to be unpacked by XP's built-in extractor and other older code.
os.PutNextEntry(newEntry);
StreamUtils.Copy(zipStream, os, new byte[4096]);
os.CloseEntry();
}
}
}
The expected result is to get back my original byte array on the server.
On the server, when it comes to the line:
Stream zipStream = zipFile.GetInputStream(zipEntry);
I receive the error 'No password available for AES encrypted stream.'
The only place I see to set a password is in the ZipOutputStream object, and I have checked at runtime, and this is set appropriately.
When unpacking, the password must be assigned to the password
-property of the ZipFile
-instance, i.e. it must be set in the RecursiveExtractRebuild
-method (for this the password has to be added as an additional parameter):
zipFile.Password = password;
as shown in this example.
It should be noted that the current DoRebuildData
-method doesn't actually unpack the data, but re-packs it into a new zip. The (optional) line in the DoRebuildData
-method:
zipOut.Password = password;
does not specify the password for the unpacking (i.e. for the old zip), but defines the password for the new zip.