I want to get the checksum of uploaded file in MVC.
Currently I am doing this
public ActionResult Index(HttpPostedFileBase file, string path)
{
if (file != null)
{
string checksumMd5 = HashGenerator.GetChecksum(file.InputStream, HashGenerator.MD5);;
string checksumSha1 = HashGenerator.GetChecksum(file.InputStream, HashGenerator.SHA1);
//other logic follows....
}
but when I do following in Console app and read file from File path then,
string path = #"C:\Users\anandv4\Desktop\Manifest-5977-681-673.txt";
var md5hash = HashGenerator.GetChecksum(path, HashGenerator.MD5);
var sha1 = HashGenerator.GetChecksum(path, HashGenerator.SHA1);
the values of both are different.
Code for generating hash :
public static string GetChecksum(string fileName, HashAlgorithm algorithm)
{
using (var stream = new BufferedStream(File.OpenRead(fileName), 1000000))
{
return BitConverter.ToString(algorithm.ComputeHash(stream)).Replace("-", string.Empty);
}
}
public static string GetChecksum(Stream stream, HashAlgorithm algorithm)
{
using (stream)
{
return BitConverter.ToString(algorithm.ComputeHash(stream)).Replace("-", string.Empty);
}
}
Can anyone explain me what is the difference between the two. Utlimately both the methods resolve to Stream in GetChecksum method
If you are hashing a stream, you need to set the current position of the stream to 0 before computing the hash.
file.InputStream.Seek(0, SeekOrigin.Begin);
For me, this is a great place for an extension method, eg.:
//compute hash using extension method:
string checksumMd5 = file.InputStream.GetMD5hash();
Which is supported by the class:
using System;
using System.IO;
public static class Extension_Methods
{
public static string GetMD5hash(this Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
using (var md5Instance = System.Security.Cryptography.MD5.Create())
{
var hashResult = md5Instance.ComputeHash(stream);
stream.Seek(0, SeekOrigin.Begin);
return BitConverter.ToString(hashResult).Replace("-", "").ToLowerInvariant();
}
}
}
Related
I am working on ASP.Net MVC 4.7 project, in which I am using Microsoft graph api to send mail.
I want to know what is the contentByte. How I can get it from the filestream. See the following code. I am searching for
fileAttachment.ContentBytes=
Note that files are in stream they are uploaded by the user.
private static MessageAttachmentsCollectionPage GetAttachments(List<HttpPostedFileBase> fileUploader)
{
var attachmentPage = new MessageAttachmentsCollectionPage();
if (fileUploader != null)
{
foreach (var file in fileUploader)
{
var fileAttachment = new FileAttachment();
fileAttachment.Name = file.FileName;
fileAttachment.ContentType = file.ContentType;
fileAttachment.ContentBytes = ??
attachmentPage.Add(fileAttachment);
}
}
This is just a base64-encoded contents of the file (string). For example:
public static class StreamExtensions
{
public static string ConvertToBase64(this Stream stream)
{
if (stream is MemoryStream memoryStream)
{
return Convert.ToBase64String(memoryStream.ToArray());
}
var bytes = new Byte[(int)stream.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(bytes, 0, (int)stream.Length);
return Convert.ToBase64String(bytes);
}
}
You may find different approaches described in the Encode a FileStream to base64 with c# thread.
I do apply a signature to a pdf document via delayed signing(SignDeferred) using IText.
The process contains the following steps:
Prepare the pdf document for siging
Reserve space for the signature in the pdf document
Create the hash value of the pdf document
Create the signature based on the hash value
Using a self signed certificate
Apply the signature to the pdf document
The whole process works and i end with a pdf document where the signature is set and is valid.
The original pdf is a PDF-A1a but the resulting pdf is not a valid PDF-A1a anymore.
I am aware that there is a documentation about IText PDF-A support (https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java/chapter-7-creating-pdf-ua-and-pdf-a-documents), but this seems to not apply since i don't change the content of the document.
My question:
How can i apply a signature using deferred signing and keep the PDF-A1a in the resulting document?
Note: If i do apply a signature directly (Without SignDeferred), the resulting pdf is still a PDF-A1a, but i do have to use SignDeferred
Note: I do use https://www.pdfen.com/pdf-a-validator for checking pdf-A
Code sample
Component used for signing:
itext.sign 7.1.5.0
itext.kernel 7.1.5.0
Component used for creating hash
BouncyCastle.Crypto 1.8.1.0
The following is a complete code sample sample with everything required in one file.
It only requires the references to itext and BouncyCastle and the path to a self signed certificate
using iText.Kernel.Pdf;
using iText.Signatures;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.IO;
namespace DeferredSigningTestConsole
{
class Program
{
static string SignatureAttributeName = "DeferredSignature";
static string CertificatePath = #"C:\temp\PDFA\PdfATestCert.2pfx.pfx";
static string CertificatePassword = "test";
static void Main(string[] args)
{
var signedPdf = SignPdf(System.IO.File.ReadAllBytes(#"C:\temp\PDFA\PDF_A1a.pdf"));
System.IO.File.WriteAllBytes(#"C:\temp\PDFA\signed.pdf", signedPdf);
}
public static byte[] SignPdf(byte[] pdfToSign)
{
byte[] hash = null;
byte[] tmpPdf = null;
//Step #1 >> prepare pdf for signing (Allocate space for the signature and calculate hash)
using (MemoryStream input = new MemoryStream(pdfToSign))
{
using (var reader = new PdfReader(input))
{
StampingProperties sp = new StampingProperties();
sp.UseAppendMode();
using (MemoryStream baos = new MemoryStream())
{
var signer = new PdfSigner(reader, baos, sp);
signer.SetCertificationLevel(PdfSigner.NOT_CERTIFIED);
signer.SetFieldName(SignatureAttributeName);
DigestCalcBlankSigner external = new DigestCalcBlankSigner(PdfName.Adobe_PPKLite, PdfName.Adbe_pkcs7_detached);
signer.SignExternalContainer(external, 121743);
hash = external.GetDocBytesHash();
tmpPdf = baos.ToArray();
}
}
//Step #2 >> Create the signature based on the document hash
byte[] signature = GetSignatureFromHash(hash);
//Step #3 >> Apply the signature to the document
ReadySignatureSigner extSigContainer = new ReadySignatureSigner(signature);
using (MemoryStream preparedPdfStream = new MemoryStream(tmpPdf))
{
using (var pdfReader = new PdfReader(preparedPdfStream))
{
using (PdfDocument docToSign = new PdfDocument(pdfReader))
{
using (MemoryStream outStream = new MemoryStream())
{
PdfSigner.SignDeferred(docToSign, SignatureAttributeName, outStream, extSigContainer);
return outStream.ToArray();
}
}
}
}
}
}
public static byte[] GetSignatureFromHash(byte[] hash)
{
FileStream fs = new FileStream(CertificatePath, FileMode.Open);
Pkcs12Store store = new Pkcs12Store(fs, CertificatePassword.ToCharArray());
String alias = "";
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
X509CertificateEntry[] chain = store.GetCertificateChain(alias);
List<Org.BouncyCastle.X509.X509Certificate> c = new List<Org.BouncyCastle.X509.X509Certificate>();
foreach (X509CertificateEntry en in chain)
{
c.Add(en.Certificate);
}
PrivateKeySignature signature = new PrivateKeySignature(pk.Key, "SHA256");
String hashAlgorithm = signature.GetHashAlgorithm();
PdfPKCS7 sgn = new PdfPKCS7(null, c.ToArray(), hashAlgorithm, false);
DateTime signingTime = DateTime.Now;
byte[] sh = sgn.GetAuthenticatedAttributeBytes(hash, null, null, PdfSigner.CryptoStandard.CMS);
byte[] extSignature = signature.Sign(sh);
sgn.SetExternalDigest(extSignature, null, signature.GetEncryptionAlgorithm());
return sgn.GetEncodedPKCS7(hash, null, null, null, PdfSigner.CryptoStandard.CMS);
}
}
internal class DigestCalcBlankSigner : IExternalSignatureContainer
{
private readonly PdfName _filter;
private readonly PdfName _subFilter;
private byte[] _docBytesHash;
internal DigestCalcBlankSigner(PdfName filter, PdfName subFilter)
{
_filter = filter;
_subFilter = subFilter;
}
internal virtual byte[] GetDocBytesHash()
{
return _docBytesHash;
}
public virtual byte[] Sign(Stream docBytes)
{
_docBytesHash = CalcDocBytesHash(docBytes);
//If we retun the signature bytes, GetAuthenticatedAttributeBytes will throw an exception
//Not clear how this should be done
return new byte[0];
}
public virtual void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, _filter);
signDic.Put(PdfName.SubFilter, _subFilter);
}
internal static byte[] CalcDocBytesHash(Stream docBytes)
{
byte[] docBytesHash = null;
docBytesHash = DigestAlgorithms.Digest(docBytes, DigestUtilities.GetDigest(DigestAlgorithms.SHA256));
return docBytesHash;
}
}
internal class ReadySignatureSigner : IExternalSignatureContainer
{
private byte[] cmsSignatureContents;
internal ReadySignatureSigner(byte[] cmsSignatureContents)
{
this.cmsSignatureContents = cmsSignatureContents;
}
public virtual byte[] Sign(Stream docBytes)
{
return cmsSignatureContents;
}
public virtual void ModifySigningDictionary(PdfDictionary signDic)
{
}
}
}
It seems the reason why the signed pdf is no longer a valid PDF-A1a is the estimated size for the signature.
I have used a value of about 120kb for the signature.
//doesn't work
signer.SignExternalContainer(external, 121743);
//does work
signer.SignExternalContainer(external, 65000);
The concept of this is documented in the ebook "Digital Signatures for PDF documents" from iText.
It seems in order to get a valid pdf-A1a the maximum size is limited to 65kb.
I now will have to test whether this works when i add a visual representation (signature image) since this was the reason i did choose such a large estimated size.
Edit:
I did some more testing and i am now able to produce valid pdf-A document with signatures:
The pdf are now valid pdf-A with the changed estimated size:
Valid with estimate size 32'000/65'000
A1a
A1b
Valid with estimate size 32'000
A2a
A2b
A2u
A3a
A3b
A3u
When adding a visual representation (image), pdf-A1a and pdf-A1b are no longer valid.
A transparent soft mask is present. Beginning with PDF 1.4 transparency is supported. Some PDF-based ISO standards prohibit the use of transparency.
But this is another problem which i now try to figure out.
I used OpenXML tools to create a byte array of the word and excel file and zip them using ZipArchive and return the filebyte.
httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new ByteArrayContent(zipFileBytes);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
httpResponseMessage.Content.Headers.ContentLength = zipFileBytes.Length;
httpResponseMessage.Content.Headers.Add("xfilename", zipFileName);
httpResponseMessage.StatusCode = HttpStatusCode.OK;
httpResponseMessage.Content.Headers.Add("Access-Control-Expose-Headers", "xfilename");
return httpResponseMessage;
The file can be download and open after unzip the zip file.
However, it cannot be review by window explorer or other unzip software.
When trying to open the document in the window explorer, error message
"Windows cannot complete the extraction. The destination file could
not be created"
Any ideas about how to solve this issue? Can the documents be review inside the zip which created by OpenXML?
UPDATE:
I'm using "Open XML SDK 2.5 Productivity Tool" to generate the code. And the code below is the one generate the document. (For detail, please use the tool to generate the code, since it is toooooooo many line of them)
using DocumentFormat.OpenXml.Packaging;
using Ap = DocumentFormat.OpenXml.ExtendedProperties;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using M = DocumentFormat.OpenXml.Math;
using Ovml = DocumentFormat.OpenXml.Vml.Office;
using V = DocumentFormat.OpenXml.Vml;
using W15 = DocumentFormat.OpenXml.Office2013.Word;
using A = DocumentFormat.OpenXml.Drawing;
using Thm15 = DocumentFormat.OpenXml.Office2013.Theme;
namespace GeneratedCode
{
public class GeneratedClass
{
// Creates a WordprocessingDocument.
public void CreatePackage(DataModel dataModel, List<DataModel> dataList, out string filename, out Byte[] fileBytes)
{
filename = string.Empty;
fileBytes = null;
using (MemoryStream ms = new MemoryStream())
{
try
{
using (WordprocessingDocument package = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document))
{
CreateParts(package, dataModel, dataList);
}
string extension = ".docx";
filename = "TestDoc" + extension;
fileBytes = ms.ToArray();
ms.Close();
return;
}
catch (System.Exception)
{
throw;
}
}
}
}
Then, I generate the zip file using the code below and passing the list of array byte from the CreatePackage function.
public byte[] zipByteDocument(List<Tuple<Byte[], string>> fileBytes)
{
// the output bytes of the zip
byte[] zipFileBytes = null;
// create a working memory stream
using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
{
// create a zip
using (System.IO.Compression.ZipArchive zip = new System.IO.Compression.ZipArchive(memoryStream, System.IO.Compression.ZipArchiveMode.Create, true))
{
// interate through the source files
foreach (Tuple<Byte[], string> file in fileBytes)
{
// add the item name to the zip
System.IO.Compression.ZipArchiveEntry zipItem = zip.CreateEntry(file.Item2);
// add the item bytes to the zip entry by opening the original file and copying the bytes
using (System.IO.MemoryStream originalFileMemoryStream = new System.IO.MemoryStream(file.Item1))
{
using (System.IO.Stream entryStream = zipItem.Open())
{
originalFileMemoryStream.CopyTo(entryStream);
}
}
}
}
zipFileBytes = memoryStream.ToArray();
}
return zipFileBytes;
}
And finally, I pass the zipFileBytes to the httpResponseMessage and it can be download. But fail to be preview without unzipping the zip file.
I've created a few unit tests (see below), which demonstrate that there should not be an issue with the code you have shared (noting, however, that I have not simply replicated your code). There is a chance that the generated code or other code that you did not share is the culprit.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using CodeSnippets.IO;
using Xunit;
namespace CodeSnippets.Tests.IO.Compression
{
public class ZipArchiveTests
{
private static byte[] CreateZipArchiveBytes(IEnumerable<(byte[], string)> files)
{
using MemoryStream stream = CreateZipArchiveStream(files);
return stream.ToArray();
}
private static MemoryStream CreateZipArchiveStream(IEnumerable<(byte[], string)> files)
{
var stream = new MemoryStream();
using (CreateZipArchive(stream, files))
return stream;
}
private static ZipArchive CreateZipArchive(Stream stream, IEnumerable<(byte[], string)> files)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (files == null) throw new ArgumentNullException(nameof(files));
var archive = new ZipArchive(stream, ZipArchiveMode.Create, true);
foreach ((byte[] fileContent, string fileName) in files)
{
ZipArchiveEntry archiveEntry = archive.CreateEntry(fileName);
using Stream entryStream = archiveEntry.Open();
entryStream.Write(fileContent, 0, fileContent.Length);
}
return archive;
}
private static ZipArchive ReadZipArchive(byte[] zipArchiveBytes)
{
return new ZipArchive(new MemoryStream(zipArchiveBytes), ZipArchiveMode.Read, false);
}
private static byte[] ReadEntryBytes(ZipArchive zipArchive, string entryName)
{
ZipArchiveEntry entry = zipArchive.GetEntry(entryName) ?? throw new Exception();
var entryBytes = new byte[entry.Length];
using Stream entryStream = entry.Open();
entryStream.Read(entryBytes, 0, (int) entry.Length);
return entryBytes;
}
private static HttpResponseMessage CreateResponseMessage(byte[] content, string fileName, string mediaType)
{
var message = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(content)
};
message.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
message.Content.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
message.Content.Headers.ContentLength = content.Length;
return message;
}
[Fact]
public async Task CreateResponseMessage_ZipArchiveBytes_Success()
{
// Arrange.
const string path = "Resources\\ZipContents.docx";
string fileName = Path.GetFileName(path);
byte[] fileContent = File.ReadAllBytes(path);
byte[] zipArchiveBytes = CreateZipArchiveBytes(new[]
{
(fileContent, fileName)
});
// Act.
using HttpResponseMessage message = CreateResponseMessage(zipArchiveBytes, "ZipArchive.zip", "application/zip");
HttpContent messageContent = message.Content;
byte[] messageBytes = await messageContent.ReadAsByteArrayAsync();
// Assert.
// Original zipArchiveBytes and recevied messageBytes are equal.
Assert.Equal(zipArchiveBytes, messageBytes);
// Original file content and received ZIP archive content are equal.
using ZipArchive zipArchive = ReadZipArchive(messageBytes);
byte[] entryContent = ReadEntryBytes(zipArchive, fileName);
Assert.Equal(fileContent.Length, entryContent.Length);
Assert.Equal(fileContent, entryContent);
}
[Fact]
public void CreateZipArchiveBytes_WordDocument_ZipFileSuccessfullyCreated()
{
// Arrange.
const string path = "Resources\\ZipContents.docx";
string fileName = Path.GetFileName(path);
byte[] fileContent = File.ReadAllBytes(path);
// Act.
byte[] zipArchiveBytes = CreateZipArchiveBytes(new[]
{
(fileContent, fileName)
});
File.WriteAllBytes("ZipArchive_Bytes.zip", zipArchiveBytes);
// Assert.
using ZipArchive zipArchive = ReadZipArchive(zipArchiveBytes);
byte[] entryContent = ReadEntryBytes(zipArchive, fileName);
Assert.Equal(fileContent.Length, entryContent.Length);
Assert.Equal(fileContent, entryContent);
}
}
}
Update 2019-12-06
I amended the unit tests to also demonstrate that this works with multiple documents. Here's the first one:
[Fact]
public void CreateZipArchiveBytes_Directory_ZipFileSuccessfullyCreated()
{
// Arrange, creating a ZIP archive with more than one entry.
List<(byte[] fileContent, string fileName)> files = Directory
.EnumerateFiles("Resources")
.Select(path => (File.ReadAllBytes(path), Path.GetFileName(path)))
.ToList();
Assert.True(files.Count > 1);
// Act.
byte[] zipArchiveBytes = CreateZipArchiveBytes(files);
File.WriteAllBytes("ZipArchive_Directory.zip", zipArchiveBytes);
// Assert.
using ZipArchive zipArchive = ReadZipArchive(zipArchiveBytes);
Assert.Equal(files.Count, zipArchive.Entries.Count);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
byte[] fileContent = files
.Where(file => file.fileName == entry.Name)
.Select(file => file.fileContent)
.Single();
using Stream entryStream = entry.Open();
byte[] entryContent = entryStream.ToArray();
Assert.Equal(fileContent, entryContent);
}
}
The next unit test demonstrates the same in conjunction with the HttpResponseMessage.
[Fact]
public async Task CreateResponseMessage_ZipArchiveDirectory_Success()
{
// Arrange, creating a ZIP archive with more than one entry.
List<(byte[] fileContent, string fileName)> files = Directory
.EnumerateFiles("Resources")
.Select(path => (File.ReadAllBytes(path), Path.GetFileName(path)))
.ToList();
Assert.True(files.Count > 1);
byte[] zipArchiveBytes = CreateZipArchiveBytes(files);
// Act.
using HttpResponseMessage message = CreateResponseMessage(zipArchiveBytes, "ZipArchive.zip", "application/zip");
HttpContent messageContent = message.Content;
byte[] messageBytes = await messageContent.ReadAsByteArrayAsync();
// Assert.
// Original zipArchiveBytes and recevied messageBytes are equal.
Assert.Equal(zipArchiveBytes, messageBytes);
// Original directory content and received ZIP archive content are equal.
using ZipArchive zipArchive = ReadZipArchive(messageBytes);
Assert.Equal(files.Count, zipArchive.Entries.Count);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
byte[] fileContent = files
.Where(file => file.fileName == entry.Name)
.Select(file => file.fileContent)
.Single();
await using Stream entryStream = entry.Open();
byte[] entryContent = await entryStream.ToArrayAsync();
Assert.Equal(fileContent, entryContent);
}
}
The core methods have not changed.
The full source code can be found in my CodeSnippets GitHub repository.
I'm coding an Universal App, how can I hash a file with md5 or SHA algorithm ?
I searched, found this: system.security.cryptography, but it's not available in my project.
I'm using Visual Studio 2015.
In UWP, it is Windows.Security.Cryptography namespace and Windows.Security.Cryptography.Core namespace.
In the CryptographicBuffer class there is sample showing how to use this class.
Here is my demo about getting MD5 hash:
private string strAlgNameUsed;
public string GetMD5Hash(String strMsg)
{
string strAlgName = HashAlgorithmNames.Md5;
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(strAlgName);
strAlgNameUsed = objAlgProv.AlgorithmName;
IBuffer buffHash = objAlgProv.HashData(buffUtf8Msg);
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
string hex = CryptographicBuffer.EncodeToHexString(buffHash);
return hex;
}
I'm going to throw this in, just because it is UWP... and if you use storageFolder.GetFileAsync to get to the file, the method will have to return an Async Task or a void. Forgive me if this isn't perfect, I'm more familiar with asp.net. But this does return a valid MD5 Hash on a file that was created in the LocalState folder:
private async Task<string> GetMD5Hash()
{
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
var storageFolder = localFolder; // await KnownFolders.GetFolderForUserAsync(null, KnownFolderId.PicturesLibrary);
var file = await storageFolder.GetFileAsync("signature.png");
byte[] computedHash = new MD5CryptoServiceProvider().ComputeHash(File.ReadAllBytes(file.Path));
var sBuilder = new StringBuilder();
foreach (byte b in computedHash)
{
sBuilder.Append(b.ToString("x2").ToLower());
}
string result = sBuilder.ToString();
return result;
}
i have a method that read some files and get hashes SHA1Managed and then compare it with other hashes from a list, how can i do this method on other thread?
public bool CheckFile(string file, string filehash)
{
if (File.Exists(file))
{
using (FileStream stream = File.OpenRead(file))
{
SHA1Managed sha = new SHA1Managed();
byte[] checksum = sha.ComputeHash(stream);
string sendCheckSum = BitConverter.ToString(checksum)
.Replace("-", string.Empty);
return sendCheckSum.ToLower() == filehash;
}
}
else return false;
}
If you just want to run it in a background thread you'd actually need to move the task creation up one level since your function returns a result. Depending on how the calling code works something like this might work for you.
var backgroundTask = Task.Factory.StartNew(() =>
{
var result = CheckFile("file", "filehash");
//do something with the result
});
Try by this codes:
public async Task<bool> CheckFile(string file, string filehash)
{
await Task.Run<bool>(()=> {
if (File.Exists(file))
{
using (FileStream stream = File.OpenRead(file))
{
SHA1Managed sha = new SHA1Managed();
byte[] checksum = sha.ComputeHash(stream);
string sendCheckSum = BitConverter.ToString(checksum)
.Replace("-", string.Empty);
return sendCheckSum.ToLower() == filehash;
}
}
else return false;
});
}