private void _WriteFileData(ZipCrypto cipher, System.IO.Stream s) { // Read in the data from the input stream (often a file in the filesystem), // and write it to the output stream, calculating a CRC on it as we go. // We will also deflate and encrypt as necessary. Stream input = null; CrcCalculatorStream input1 = null; CountingStream counter = null; try { // s.Position may fail on some write-only streams, eg stdout or System.Web.HttpResponseStream // We swallow that exception, because we don't care! this.__FileDataPosition = s.Position; } catch{} try { // get the original stream: if (_sourceStream != null) { _sourceStream.Position = 0; input = _sourceStream; } else { input = System.IO.File.OpenRead(LocalFileName); } // wrap a CRC Calculator Stream around the raw input stream. input1 = new CrcCalculatorStream(input); // wrap a counting stream around the raw output stream: counter = new CountingStream(s); // maybe wrap an encrypting stream around that: Stream output1 = (Encryption == EncryptionAlgorithm.PkzipWeak) ? (Stream)(new ZipCipherStream(counter, cipher, CryptoMode.Encrypt)) : counter; // maybe wrap a DeflateStream around that Stream output2 = null; bool mustCloseDeflateStream = false; if (CompressionMethod == 0x08) { output2 = new DeflateStream(output1, CompressionMode.Compress, true); mustCloseDeflateStream = true; } else output2 = output1; // as we emit the file, we maybe deflate, then maybe encrypt, then write the bytes. byte[] buffer = new byte[READBLOCK_SIZE]; int n = input1.Read(buffer, 0, READBLOCK_SIZE); while (n > 0) { output2.Write(buffer, 0, n); n = input1.Read(buffer, 0, READBLOCK_SIZE); } // by calling Close() on the deflate stream, we write the footer bytes, as necessary. if (mustCloseDeflateStream) output2.Close(); } finally { if (_sourceStream == null && input != null) { input.Close(); input.Dispose(); } } _UncompressedSize = input1.TotalBytesSlurped; _CompressedSize = counter.BytesWritten; _Crc32 = input1.Crc32; if ((_Password != null) && (Encryption == EncryptionAlgorithm.PkzipWeak)) { _CompressedSize += 12; // 12 extra bytes for the encryption header } int i = 8; _EntryHeader[i++] = (byte)(CompressionMethod & 0x00FF); _EntryHeader[i++] = (byte)((CompressionMethod & 0xFF00) >> 8); i = 14; // CRC - the correct value now _EntryHeader[i++] = (byte)(_Crc32 & 0x000000FF); _EntryHeader[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); _EntryHeader[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); _EntryHeader[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); // CompressedSize - the correct value now _EntryHeader[i++] = (byte)(_CompressedSize & 0x000000FF); _EntryHeader[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); _EntryHeader[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); _EntryHeader[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); // UncompressedSize - the correct value now _EntryHeader[i++] = (byte)(_UncompressedSize & 0x000000FF); _EntryHeader[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); _EntryHeader[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); _EntryHeader[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); // workitem 6414 if (s.CanSeek) { // seek in the raw output stream, to the beginning of the header for this entry. s.Seek(this._RelativeOffsetOfHeader, System.IO.SeekOrigin.Begin); // finally, write the updated header to the output stream s.Write(_EntryHeader, 0, _EntryHeader.Length); // adjust the count on the CountingStream as necessary var s1 = s as CountingStream; if (s1 != null) s1.Adjust(_EntryHeader.Length); // seek in the raw output stream, to the end of the file data for this entry s.Seek(_CompressedSize, System.IO.SeekOrigin.Current); } else { // eg, ASP.NET Response.OutputStream, or stdout if ((_BitField & 0x0008) != 0x0008) throw new ZipException("Logic error."); byte[] Descriptor = new byte[16]; i = 0; // signature int sig = ZipConstants.ZipEntryDataDescriptorSignature; Descriptor[i++] = (byte)(sig & 0x000000FF); Descriptor[i++] = (byte)((sig & 0x0000FF00) >> 8); Descriptor[i++] = (byte)((sig & 0x00FF0000) >> 16); Descriptor[i++] = (byte)((sig & 0xFF000000) >> 24); // CRC - the correct value now Descriptor[i++] = (byte)(_Crc32 & 0x000000FF); Descriptor[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); Descriptor[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); Descriptor[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); // CompressedSize - the correct value now Descriptor[i++] = (byte)(_CompressedSize & 0x000000FF); Descriptor[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); Descriptor[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); Descriptor[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); // UncompressedSize - the correct value now Descriptor[i++] = (byte)(_UncompressedSize & 0x000000FF); Descriptor[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); Descriptor[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); Descriptor[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); // finally, write the updated header to the output stream s.Write(Descriptor, 0, Descriptor.Length); } }
private Int32 _ExtractOne(System.IO.Stream output, ZipCrypto cipher) { System.IO.Stream input = this.ArchiveStream; // seek to the beginning of the file data in the stream input.Seek(this.__FileDataPosition, System.IO.SeekOrigin.Begin); // to validate the CRC. Int32 CrcResult = 0; byte[] bytes = new byte[READBLOCK_SIZE]; // The extraction process varies depending on how the entry was stored. // It could have been encrypted, and it coould have been compressed, or both, or // neither. So we need to check both the encryption flag and the compression flag, // and take the proper action in all cases. int LeftToRead = (CompressionMethod == 0x08) ? this.UncompressedSize : this._CompressedFileDataSize; // get a stream that either decrypts or not. Stream input2 = (Encryption == EncryptionAlgorithm.PkzipWeak) ? new ZipCipherStream(input, cipher, CryptoMode.Decrypt) : input; // using the above, now we get a stream that either decompresses or not. Stream input3 = (CompressionMethod == 0x08) ? new DeflateStream(input2, CompressionMode.Decompress, true) : input2; //var out2 = new CrcCalculatorStream(output, LeftToRead); // as we read, we maybe decrypt, and then we maybe decompress. Then we write. using (var s1 = new CrcCalculatorStream(input3)) { while (LeftToRead > 0) { int len = (LeftToRead > bytes.Length) ? bytes.Length : LeftToRead; int n = s1.Read(bytes, 0, len); _CheckRead(n); output.Write(bytes, 0, n); LeftToRead -= n; } CrcResult = s1.Crc32; } return CrcResult; }
private ZipCrypto SetupCipher(string password) { ZipCrypto cipher = null; // decrypt the file header data here if necessary. if (Encryption == EncryptionAlgorithm.PkzipWeak) { if (password == null) throw new BadPasswordException("This entry requires a password."); cipher = new ZipCrypto(); cipher.InitCipher(password); // Decrypt the header. This has a side effect of "further initializing the // encryption keys" in the traditional zip encryption. byte[] DecryptedHeader = cipher.DecryptMessage(_WeakEncryptionHeader, _WeakEncryptionHeader.Length); // CRC check // According to the pkzip spec, the final byte in the decrypted header // is the highest-order byte in the CRC. We check it here. if (DecryptedHeader[11] != (byte)((_Crc32 >> 24) & 0xff)) { // In the case that bit 3 of the general purpose bit flag is set to indicate // the presence of an 'Extended File Header', the last byte of the decrypted // header is sometimes compared with the high-order byte of the lastmodified // time, and not the CRC, to verify the password. // // This is not documented in the PKWare Appnote.txt. // This was discovered this by analysis of the Crypt.c source file in the InfoZip library // http://www.info-zip.org/pub/infozip/ if ((_BitField & 0x0008) != 0x0008) { throw new BadPasswordException("The password did not match."); } else if (DecryptedHeader[11] != (byte)((_TimeBlob >> 8) & 0xff)) { throw new BadPasswordException("The password did not match."); } } // We have a good password. } return cipher; }
private void _EmitOne(System.IO.Stream outstream, out ZipCrypto cipher) { // If PKZip (weak) encryption is in use, then the entry data is preceded by // 12-byte "encryption header" for the entry. byte[] encryptionHeader = null; cipher = null; if (_Password != null && Encryption == EncryptionAlgorithm.PkzipWeak) { cipher = new ZipCrypto(); // apply the password to the keys cipher.InitCipher(_Password); // generate the random 12-byte header: var rnd = new System.Random(); encryptionHeader = new byte[12]; rnd.NextBytes(encryptionHeader); // Here, it is important to encrypt the random header, INCLUDING the final byte // which is the high-order byte of the CRC32. We must do this before // we encrypt the file data. This step changes the state of the cipher, or in the // words of the PKZIP spec, it "further initializes" the cipher keys. // No way around this: must read the stream to compute the actual CRC FigureCrc32(); encryptionHeader[11] = (byte)((this._Crc32 >> 24) & 0xff); byte[] cipherText = cipher.EncryptMessage(encryptionHeader, encryptionHeader.Length); // Write the ciphered bonafide encryption header. outstream.Write(cipherText, 0, cipherText.Length); } // write the (potentially compressed, potentially encrypted) file data _WriteFileData(cipher, outstream); _TotalEntrySize = _LengthOfHeader + _CompressedSize; }
/// <summary> /// The constructor. /// </summary> /// <param name="s">The underlying stream</param> /// <param name="mode">To either encrypt or decrypt.</param> /// <param name="cipher">The pre-initialized ZipCrypto object.</param> public ZipCipherStream(System.IO.Stream s, ZipCrypto cipher, CryptoMode mode) : base() { _cipher = cipher; _s = s; _mode = mode; }