/// <summary> /// Read the encrypted file header information from a stream /// </summary> /// <param name="stream">The stream to read</param> /// <returns>The encrypted header object</returns> public static EncryptedFileHeader ReadFromStream(Stream stream) { Pre.ThrowIfArgumentNull(stream, nameof(stream)); // Read the entire header from the stream byte[] buffer = stream.ReadByteArray(HeaderSize); // Verify the header's checksum using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) { byte[] computedHash = sha1.ComputeHash(buffer, 0, HeaderSize - ChecksumSize); byte[] headerHash = BufferUtil.CopyBytes(buffer, buffer.Length - ChecksumSize, ChecksumSize); if (!NativeMethods.ByteArrayEquals(computedHash, headerHash)) { // TODO: Replace with a better exception throw new Exception("The header checksum failed"); } } // Create a new stream for reading the header data. This will make it easier to process the // fields in the header and will avoid having to reposition the original stream. using (MemoryStream headerStream = new MemoryStream(buffer)) { EncryptedFileHeader header = new EncryptedFileHeader(); int encryptedKeyLength = headerStream.ReadInt32(); // Ensure the key is not longer than the buffer Pre.Assert(encryptedKeyLength < HeaderSize, "encryptedKeyLength < HeaderSize"); // Read the encrypted key field. header.EncryptedKey = headerStream.ReadByteArray(encryptedKeyLength); // Read the IV length. This should be 16 bytes (asserted below). int ivLength = headerStream.ReadInt32(); Pre.Assert(ivLength == IVStorageSize, "ivLength == ivStorageSize"); // Read the initialization vector header.IV = headerStream.ReadByteArray(ivLength); // Read the file sizes, thumbprint, and padding header.OriginalFileLength = headerStream.ReadInt64(); header.EncryptedFileLength = headerStream.ReadInt64(); header.CertificateThumbprint = headerStream.ReadByteArray(CertificateThumbprintSize); header.PaddingLength = headerStream.ReadInt16(); return(header); } }