/// <summary> /// Tries to decrypt the file <paramref name="encryptedFile"/>, using the private hashed hashedKey /// </summary> /// <param name="encryptedFile"></param> /// <param name="key"></param> public static FileInfo Decrypt(FileInfo encryptedFile, string key) { if (encryptedFile == null) { throw new ArgumentNullException(nameof(encryptedFile)); } if (key == null) { throw new ArgumentNullException(nameof(key)); } // ready encryption hashedKey and info EncryptionInfo info; // obtaining original json header using (var encryptedFs = encryptedFile.OpenText()) { var js = new JsonSerializer { CheckAdditionalContent = false }; info = (EncryptionInfo)js.Deserialize(encryptedFs, typeof(EncryptionInfo)); } byte[] hashedKey; EncryptKey(key, info, out hashedKey); // decrypting the file // prevent conflict with any existing file var newFile = Core.GetNonCollidingFile(Path.Combine(encryptedFile.DirectoryName, Path.GetFileNameWithoutExtension(encryptedFile.Name))); using (var tempFiles = new TempFileGenerator()) { var zippedFile = tempFiles.CreateFile(); // find the "end" of the JSON header var encryptedPortionLength = SeekEndOfJsonHeader(encryptedFile); // decrypting to temporary gzipped file using (var encryptedFs = encryptedFile.OpenRead()) using (var crypter = Activator.CreateInstance(info.EncryptionAlgorithm) as SymmetricAlgorithm) { // loading cryptography parameters if (crypter == null) { throw new CryptographicException($"Unknown Symmetric Algorithm: {info.EncryptionAlgorithm}"); } crypter.KeySize = info.KeySize; crypter.BlockSize = info.BlockSize; crypter.Key = hashedKey; crypter.IV = info.Iv; // readying the encrypted file stream to start reading after the json header encryptedFs.Seek(encryptedPortionLength, SeekOrigin.Begin); using ( var cs = new CryptoStream(encryptedFs, crypter.CreateDecryptor(), CryptoStreamMode.Read)) using (var zippedFileStream = zippedFile.OpenWrite()) cs.CopyTo(zippedFileStream); } // delete hashed key Core.ShallowEraseList(hashedKey); // unzip from the temporary file into the final permanent file using (var zippedFs = zippedFile.OpenRead()) using (var newFs = newFile.Open(FileMode.Create, FileAccess.Write)) using (var zipper = new GZipStream(zippedFs, CompressionMode.Decompress)) zipper.CopyTo(newFs); // safely delete the zipped file, as it shouldn't stay in the OS like that SafeOverwriteFile(zippedFile); zippedFile.Delete(); } // check the hash of the final product, must match to the hash stored in the header var newHash = Hash(newFile, info.HashAlgorithm); if (newHash.Equals(info.OriginalHash, StringComparison.CurrentCultureIgnoreCase)) { return(newFile); } throw new CryptographicException($"Checksum failed: Original was {info.OriginalHash}, obtained {newHash}"); }
/// <summary> /// Encrypts the given file, using the private hashed hashedKey /// </summary> /// <param name="originalFile"></param> /// <param name="key"></param> /// <param name="deleteOriginalSafely"></param> public static FileInfo Encrypt(FileInfo originalFile, string key, bool deleteOriginalSafely = false) { if (originalFile == null) { throw new ArgumentNullException(nameof(originalFile)); } if (!originalFile.Exists) { throw new ArgumentException($"{nameof(originalFile)} points to a non-existant file"); } if (Directory.Exists(originalFile.FullName)) { throw new ArgumentException($"{nameof(originalFile)} points to a folder, not a file"); } // ready encryption hashedKey and info var info = new EncryptionInfo { Version = Application.ProductVersion, HashAlgorithm = typeof(SHA256CryptoServiceProvider), EncryptionAlgorithm = typeof(AesCryptoServiceProvider), KeySize = 256, BlockSize = 128, SaltSize = 128 }; byte[] hashedKey; EncryptKey(key, info, out hashedKey); // hash original file var hash = Hash(originalFile, info.HashAlgorithm); var newFile = Core.GetNonCollidingFile(originalFile.FullName + Settings.Default.Extension); // encrypt original file with info header in the start using (var tempFiles = new TempFileGenerator()) using (var crypter = new AesCryptoServiceProvider()) { // load hashedKey and IV into cryptography service crypter.Key = hashedKey; crypter.GenerateIV(); //Debug.Assert(crypter.ValidKeySize(256)); // create temporary files var zippedFile = tempFiles.CreateFile(); // zip original file into temporary zipped file using (var zippedFs = zippedFile.OpenWrite()) using (var zipper = new GZipStream(zippedFs, CompressionMode.Compress)) using (var originalFs = originalFile.OpenRead()) originalFs.CopyTo(zipper); //progressBar.BeginInvoke(new Action(() => { progressBar.Increment(5); })); // add Json header to final file with a text stream using (var newFs = newFile.CreateText()) { info.OriginalHash = hash; info.Iv = crypter.IV; newFs.Write(JsonConvert.SerializeObject(info)); } // encrypt zipped file into final file, as an append using (var zippedFs = zippedFile.OpenRead()) using ( var cs = new CryptoStream(zippedFs, crypter.CreateEncryptor(), CryptoStreamMode.Read)) using (var newFs = newFile.Open(FileMode.Open, FileAccess.ReadWrite)) { newFs.Seek(0, SeekOrigin.End); cs.CopyTo(newFs); } // delete hashed key Core.ShallowEraseList(hashedKey); // safely delete the zipped file, as it shouldn't stay in the OS like that SafeOverwriteFile(zippedFile); zippedFile.Delete(); } // safe deleting of the original file if (!deleteOriginalSafely) { return(newFile); } SafeOverwriteFile(originalFile); originalFile.Delete(); return(newFile); }