/// <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); } }
private void ReadFirstEncryptedBlock(byte[] inputBuffer, ref int offset, ref int count) { // Create a temporary stream for reading the input buffer. This will make it easier to read the // input buffer and preserve the read position in the buffer. using (MemoryStream inputBufferStream = new MemoryStream(inputBuffer, offset, count)) { EncryptedFileHeader header = EncryptedFileHeader.ReadFromStream(inputBufferStream); // Decrypt the key. byte[] key = this.rsa.Decrypt(header.EncryptedKey, false); // Create the decrypter used to decrypt the file this.cryptoTransform = this.aes.CreateDecryptor(key, header.IV); // Update the count and offset based on the size read for the header since these will // be used below to decrypt the buffer. count -= (int)inputBufferStream.Position - offset; offset = (int)inputBufferStream.Position; } this.firstBlockTransformed = true; }
private void WriteFirstEncryptedBlock(MemoryStream bufferedOutputStream) { // Create the encryptor that will be used to transform the data. This will use the key and IV // that were generated when the aes object was created, and will only be used for this stream. this.cryptoTransform = this.aes.CreateEncryptor(); RSAPKCS1KeyExchangeFormatter keyFormatter = new RSAPKCS1KeyExchangeFormatter(this.rsa); byte[] keyEncrypted = keyFormatter.CreateKeyExchange(this.aes.Key, this.aes.GetType()); EncryptedFileHeader header = new EncryptedFileHeader { EncryptedKey = keyEncrypted, IV = this.aes.IV, CertificateThumbprint = AdapterBase.HexToBytes(this.encryptionCertificate.Thumbprint) }; short padding; if (this.Mode == EncryptionMode.Encrypt) { header.OriginalFileLength = this.sourceFileSize; header.EncryptedFileLength = CalculateEncryptedFileSize(this.sourceFileSize, out padding); header.PaddingLength = padding; } else { header.EncryptedFileLength = this.sourceFileSize; header.OriginalFileLength = CalculateDecryptedFileSize(this.sourceFileSize, out padding); header.PaddingLength = padding; } header.WriteToStream(bufferedOutputStream); this.firstBlockTransformed = true; }