void bw_DoWork(object sender, DoWorkEventArgs e) { CryptArgs args = (CryptArgs)e.Argument; BgwProgressUpdater bgw = new BgwProgressUpdater((BackgroundWorker)sender); FileCrypter.EncryptFile(args.InputPath, args.OutputPath, args.Password, args.IsDirectory, args.CompressionMode, bgw); }
/// <summary> /// Work of the DECRYPT AND SAVE backgorund worker. /// </summary> void bwSave_DoWork(object sender, DoWorkEventArgs e) { CryptArgs args = (CryptArgs)e.Argument; BgwProgressUpdater bgw = new BgwProgressUpdater((BackgroundWorker)sender); FileCrypter.DecryptFile(args.InputPath, args.OutputPath, args.Password, bgw); }
/// <summary> /// Work of the OPEN TEMP background worker. /// </summary> void bwOpenTemp_DoWork(object sender, DoWorkEventArgs e) { TempCryptArgs args = (TempCryptArgs)e.Argument; BgwProgressUpdater bgw = new BgwProgressUpdater((BackgroundWorker)sender); TempDecryptFile tDecryptFile = new TempDecryptFile(args.InputFilePath, args.Password, bgw, this); tDecryptFile.Run(); }
/// <summary> /// Initialize a temporarily decrypted file manager. /// </summary> /// <param name="path">Path of the file to decrypt and manage.</param> /// <param name="password">Password of the file.</param> /// <param name="bgWorker">Optional. Indicate a background worker if you want to know the progress of the manager's work.</param> public TempDecryptFile(string inputFilePath, string password, BgwProgressUpdater bgw, Form owner) { InputFilePath = inputFilePath; Password = password; Owner = owner; if (!File.Exists(inputFilePath)) { throw new FileNotFoundException(); } Bgw = bgw; }
public static readonly int _readBufferSize = 268435456; // Buffer size of 0.25 GB ////// METADATA ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ////// Format du Body : Version de MCrypt, Type[File/Directory], OriginalName, Hash, Salt, Iterations, BatchesLength, CompressionMode public static CrypterInfo EncryptFile(string inputPath, string outputDirectory, string password, bool isDirectory, CompressionMode compressionMode, BgwProgressUpdater bgw) { Output.Print("ENCRYPTING: " + inputPath); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); string finalInputPath = ""; // Needed for finally string outputFilePath = ""; try { // Création de outputFilePath outputFilePath = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(inputPath) + (isDirectory? ".mcryptfolder": ".mcryptfile")); Output.Print("Output file path: " + outputFilePath); // MANAGE DIRECTORIES bgw.ProgressChanged(0, lang.CompressingData); finalInputPath = Files.GetTempFilePath(); if (!isDirectory) { Output.Print("Encrypting a file."); Output.Print(string.Format("Zipping file in temp file. ({0})", finalInputPath)); Zipper.ZipOneFile(inputPath, finalInputPath, compressionMode); } else { Output.Print("Encrypting a directory."); Output.Print(string.Format("Zipping directory in temp file. ({0})", finalInputPath)); Zipper.ZipDirectory(inputPath, finalInputPath, compressionMode); } bgw.ProgressChanged(20, lang.PreparingEncryption); // Set Aes AesManaged aes = new AesManaged(); aes.BlockSize = aes.LegalBlockSizes[0].MaxSize; aes.KeySize = aes.LegalKeySizes[0].MaxSize; // Set key, iv and mode // NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be exactly the same, including order, on both the encryption and decryption sides. // Salt can be public. We will use the BCrypt salt generator, and then convert it to bytes. string saltString = BCrypt.Net.BCrypt.GenerateSalt(); byte[] salt = Encoding.Default.GetBytes(saltString); Output.Print("Salt: " + saltString); Output.Print("Iterations: " + _iterations); // Obtention du mot de passe dérivé en clé, utilisée pour le cryptage. Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, _iterations); aes.Key = key.GetBytes(aes.KeySize / 8); // Clé de cryptage (obtenue à partir d'une dérivation de mot de passe) aes.IV = key.GetBytes(aes.BlockSize / 8); // Vecteur d'initialisation (issu de la clé) aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; // Création de l'encrypteur ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV); // Génération du hash BCrypt du mot de passe, que l'on stockera dans le metadata. string passwordHash = BCrypt.Net.BCrypt.EnhancedHashPassword(password); Output.Print("Password hash: " + passwordHash); // Génération du originalName string originalName = Path.GetFileName(inputPath); Output.Print("Original name: " + originalName); // Delete possible existing output file try { File.Delete(outputDirectory); } catch { } //// ENCRYPT using (FileStream source = new FileStream(finalInputPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (FileStream destination = new FileStream(outputFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { // Writing encrypted file data // Note : Metadata will be insterted at the end of the file (batches length needed) // Encrypting BufferSize MB batches long readBytes = 0; long totalReadBytes = source.Length; int batchesNumber = (int)Math.Ceiling(totalReadBytes / (double)_readBufferSize); Output.Print("Total bytes to encrypt: " + totalReadBytes); Output.Print("Number of batches: " + batchesNumber); int batches = 0; long encryptedBytes = 0; long[] batchesLength = new long[batchesNumber]; Output.Print("Encrypting batches..."); while (readBytes < totalReadBytes) { bgw.ProgressChanged(20 + batches * 60 / batchesNumber, string.Format(lang.Encrypting + " (" + lang.step + " {0} / {1})", batches + 1, batchesNumber)); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(ms, transform, CryptoStreamMode.Write)) // we create a cryptoStream for each batch (because it's of single use) { int bytesToRead; if (totalReadBytes - readBytes > _readBufferSize) { bytesToRead = _readBufferSize; } else { bytesToRead = (int)(totalReadBytes - readBytes); } byte[] readBuffer = new byte[bytesToRead]; source.Read(readBuffer, 0, bytesToRead); cryptoStream.Write(readBuffer, 0, bytesToRead); cryptoStream.FlushFinalBlock(); readBytes += bytesToRead; destination.Write(ms.ToArray(), 0, (int)ms.Length); ms.Flush(); batchesLength[batches] = ms.Length; } } GC.Collect(); encryptedBytes += batchesLength[batches]; batches++; Output.Print(string.Format("Batch number {0} / {1} done. ({2} encrypted bytes)", batches, batchesNumber, batchesLength[batches - 1])); } Output.Print(string.Format("Total encrypted bytes: {0}", encryptedBytes)); bgw.ProgressChanged(80, lang.WritingMetadata); Output.Print("Building metadata."); // Building metadataBatchesLength StringBuilder metadataBatchesLengthString = new StringBuilder(); for (int i = 0; i < batchesNumber; i++) { metadataBatchesLengthString.Append(batchesLength[i] + "/"); } Output.Print("Metadata batches length: " + metadataBatchesLengthString); string metadataString = string.Format("{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|", Application.ProductVersion, isDirectory.ToString(), originalName, passwordHash, saltString, _iterations, metadataBatchesLengthString, (int)compressionMode); byte[] metadata = Encoding.Default.GetBytes(metadataString); metadataString = string.Format("{0}MCryptEncryptedData|{1}", metadataString, metadata.Count().ToString().PadLeft(10, '0')); metadata = Encoding.Default.GetBytes(metadataString); Output.Print(string.Format("METADATA : {0} ({1} bytes)", metadataString, metadata.Length)); // Writing metadata destination.Write(metadata, 0, metadata.Length); Output.Print("Metadata written at the end of the file."); } } bgw.ProgressChanged(100, lang.EncryptionSuccessful); Output.Print("Encryption successful!"); } catch (Exception e) { throw new EncryptException(e.Message, e.InnerException); } finally { stopwatch.Stop(); Output.Print(string.Format("Operation ended in {0} .", stopwatch.Elapsed)); if (File.Exists(finalInputPath)) { Files.OptimalFileDelete(finalInputPath); Output.Print("Zipped temp file deleted."); } } return(new CrypterInfo(outputFilePath, isDirectory, compressionMode)); }
public static CrypterInfo DecryptFile(string inputFilePath, string outputDirectory, string password, BgwProgressUpdater bgw) { Output.Print("DECRYPTING: " + inputFilePath); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); // Needed for return bool isDirectory = false; string userOutputPath = ""; CompressionMode compressionMode; try { // DECRYPT PROCESS using (FileStream source = new FileStream(inputFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { bgw.ProgressChanged(5, lang.ReadingMetadata); // Récupération du Header byte[] metadataHeader = new byte[_metadataHeaderLength]; // Simplement "MCryptEncryptedData_{taille d'octets à lire}" source.Seek(-_metadataHeaderLength, SeekOrigin.End); source.Read(metadataHeader, 0, _metadataHeaderLength); // Vérification si ce fichier est bien un fichier MCrypt try { string metadataHeaderStringCheck = Encoding.Default.GetString(metadataHeader); //Console.WriteLine(metadataHeaderString); if (metadataHeaderStringCheck.Split('|')[0] != "MCryptEncryptedData") { Output.Print("Metadata header incorrect. This is not a MCrypt file.", Level.Error); throw new Exception(); } } catch // Si une erreur survient, c'est que ce n'est pas { source.Flush(); source.Close(); throw new NotMCryptFileException(); } Output.Print("MCrypt file detected!"); // Récupération de la fin du Header pour avoir le nombre de caractères du Body string metadataHeaderString = Encoding.Default.GetString(metadataHeader); int metadataBodyLength = int.Parse(metadataHeaderString.Split('|')[1]); Output.Print("Metadata Body Length: " + metadataBodyLength); byte[] metadataBody = new byte[metadataBodyLength]; source.Seek(-metadataBodyLength - _metadataHeaderLength, SeekOrigin.Current); source.Read(metadataBody, 0, metadataBodyLength); string[] metadataBodyStringSplit = Encoding.Default.GetString(metadataBody).Split('|'); //Vérification de la version Version mcryptVersion = new Version(metadataBodyStringSplit[0]); if (mcryptVersion <= Version.Parse("1.2.0.0")) { Output.Print(string.Format("File encrypted with a previous version of MCrypt ({0}). Decrypting with Osbolete_DecryptFile120.", mcryptVersion)); source.Flush(); source.Close(); return(Obsolete_DecryptFile120(inputFilePath, outputDirectory, password)); } Output.Print("Current MCrypt version!"); // Vérification du mot de passe bgw.ProgressChanged(10, lang.CheckingPassword); string passwordHash = metadataBodyStringSplit[3]; if (!BCrypt.Net.BCrypt.EnhancedVerify(password, passwordHash)) { throw new WrongPasswordException(); } Output.Print("Correct password!"); // Build output path bgw.ProgressChanged(15, lang.PreparingDecryption); // Manage encrypted directories isDirectory = bool.Parse(metadataBodyStringSplit[1]); userOutputPath = Path.Combine(outputDirectory, metadataBodyStringSplit[2]); // The path for user output. Output.Print(string.Format("Built User Output Path: ({0})", userOutputPath)); string outputFilePath = Files.GetTempFilePath(); if (!isDirectory) { Output.Print("Decrypting a file."); } else { Output.Print("Decrypting a directory."); } Output.Print(string.Format("Decrypting compressed data in temp file. ({0})", outputFilePath)); // Récupération du salt byte[] salt = Encoding.Default.GetBytes(metadataBodyStringSplit[4]); // Récupération des itérations int iterations = int.Parse(metadataBodyStringSplit[5]); // Récupération des Batches Length Output.Print("Batches Length: " + metadataBodyStringSplit[6]); string[] metadataReadBufferSizeStringSplit = metadataBodyStringSplit[6].Split('/'); long[] readBatchesLength = new long[metadataReadBufferSizeStringSplit.Length - 1]; for (int i = 0; i < metadataReadBufferSizeStringSplit.Length - 1; i++) // (- 1 car le dernier est vide) { readBatchesLength[i] = long.Parse(metadataReadBufferSizeStringSplit[i]); } // Récupéartion du compressionMode compressionMode = (CompressionMode)int.Parse(metadataBodyStringSplit[7]); // Set Aes AesManaged aes = new AesManaged(); aes.BlockSize = aes.LegalBlockSizes[0].MaxSize; aes.KeySize = aes.LegalKeySizes[0].MaxSize; // Set key, iv & mode // NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides. Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations); aes.Key = key.GetBytes(aes.KeySize / 8); aes.IV = key.GetBytes(aes.BlockSize / 8); aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; // Create decryptor ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV); // Décryptage using (FileStream destination = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { // Remise à zéro du flux !!! source.Seek(0, SeekOrigin.Begin); long readBytes = 0; long totalReadBytes = source.Length - (_metadataHeaderLength + metadataBodyLength); double batchesNumber = readBatchesLength.Length; Output.Print("Total bytes to decrypt: " + totalReadBytes); Output.Print("Number of batches: " + batchesNumber); int batches = 0; long decryptedBytes = 0; Output.Print("Decrypting batches..."); for (int i = 0; i < batchesNumber; i++) { bgw.ProgressChanged(20 + (int)(i * 60 / batchesNumber), string.Format(lang.Decrypting + " (" + lang.step + " {0} / {1})", i + 1, batchesNumber)); using (MemoryStream ms = new MemoryStream()) { using (CryptoStream cryptoStream = new CryptoStream(ms, transform, CryptoStreamMode.Write)) // we create a cryptoStream for each batch (because it's of single use) { int bytesToRead = (int)readBatchesLength[i]; byte[] readBuffer = new byte[bytesToRead]; source.Read(readBuffer, 0, bytesToRead); cryptoStream.Write(readBuffer, 0, bytesToRead); cryptoStream.FlushFinalBlock(); readBytes += bytesToRead; decryptedBytes += ms.Length; destination.Write(ms.ToArray(), 0, (int)ms.Length); ms.Flush(); } } batches++; Output.Print(string.Format("Batch number {0} / {1} done. ({2} bytes)", batches, batchesNumber, readBatchesLength[i])); GC.Collect(); // Nécessaire !!!!! } Output.Print(string.Format("Total decrypted bytes: {0}", decryptedBytes)); } bgw.ProgressChanged(80, lang.DecompressingData); Zipper.Unzip(outputFilePath, isDirectory? userOutputPath : Path.GetDirectoryName(userOutputPath)); // If this is a single file, don't create a folder with its name, just decrypt it directly. Files.OptimalFileDelete(outputFilePath); Output.Print("Unzipped and deleted temp file containing compressed data."); } bgw.ProgressChanged(100, lang.DecryptionSuccessful); Output.Print("Decryption successful!"); } catch (WrongPasswordException e) { throw e; } catch (Exception e) { throw new DecryptException(e.Message, e.InnerException); } finally { stopwatch.Stop(); Output.Print(string.Format("Operation ended in {0} .", stopwatch.Elapsed)); } return(new CrypterInfo(userOutputPath, isDirectory, compressionMode)); }