Search code examples
c#phpzipdownloadchecksum

Download file and check integrity


I have a server running PHP on which i can upload zip archives using a custom form in administration panel. When I upload a zip archive, I calculate a CRC32 checksum of it and I insert it into my database for future download integrity checks:

$zipchecksum = strtoupper(hash_file('crc32', $zippath));
$mysqli->query("INSERT INTO files VALUES (NULL, '$zipname', '$zippath')");

When I query the server for an available file to download using my C# client, I also receive that checksum in the response and if a new file is available, I download it:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Properties.Settings.Default.URLDownload);
request.Accept = "application/octet-stream";
request.ContentType = "application/x-www-form-urlencoded";
request.Method = WebRequestMethods.Http.Post;

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (MemoryStream memoryStream = new MemoryStream())
{
    responseStream.CopyTo(memoryStream);

    Crc32 crc = new Crc32();
    crc.Update(memoryStream.GetBuffer());

    if (crc.Value.ToString("X") != m_ServerData.CRC)
        throw new Exception("Malformed zip file received!");

    ZipFile file = new ZipFile(memoryStream);
    // ...
}

And here is my response:

header('Cache-Control: must-revalidate, pre-check=0, post-check=0');
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename="'.$zipname.'"');
header('Content-Length: '.filesize($filepath));
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/octet-stream');
header('Expires: 0');
header('Pragma: public');

readfile($zippath);

Now, here comes the integrity check. The CRC32 checksum calculated by PHP is completely different from the one I calculate in my client using the Crc32 class from ICSharpCode.SharpZipLib. For example:

PHP Server -> CF83A609

C# Client -> 3FB78619

I also tried to use another PHP function:

$zipcontents = file_get_contents($zippath); 
$crc = crc32($zipcontents);
$zipchecksum = strtoupper(dechex(crc32($zipcontents)));
$mysqli->query("INSERT INTO files VALUES (NULL, '$zipname', '$zippath')");

But the result is once again different.

PHP Server -> A6A642D0

C# Client -> 3FB78619

So it seems like the buffer that PHP is using to calculate the checksum differs from the one the client receives from server. Well... anyone has a solution?


Solution

  • I had a similar issue, the problem wasn't the checksum algorithm.
    The problem was the downloaded file was different from the uploaded file.

    First of all, I would check that the file upload is he same you stored in the ddbb.

    In MySQL: SELECT fileName, md5(fileBlob) FROM yourTable;

    Second, I would check that the file you downloaded is the same you uploaded.

    You could check the integrity here http://onlinemd5.com/

    I did realize that the REST I was developing was doing a wrong conversion.

    I know it's late but Hope it helps.