/// <summary> /// The constructor. /// </summary> /// <param name="s">The underlying stream</param> /// <param name="cryptoParams">The pre-initialized WinZipAesCrypto object.</param> /// <param name="length">The maximum number of bytes to read from the stream.</param> internal WinZipAesCipherStream(Stream s, WinZipAesCrypto cryptoParams, long length) : this(s, cryptoParams) { // don't read beyond this limit! _length = length; //Console.WriteLine("max length of AES stream: {0}", _length); }
internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams) : base() { _params = cryptoParams; _s = s; _nonce = 1; if (_params == null) { throw new BadPasswordException("Supply a password to use AES encryption."); } int keySizeInBits = _params.KeyBytes.Length * 8; if (keySizeInBits != 256 && keySizeInBits != 128 && keySizeInBits != 192) { throw new ArgumentOutOfRangeException("cryptoParams", "size of key must be 128, 192, or 256"); } _mac = new HMACSHA1(_params.MacIv); _aesCipher = new System.Security.Cryptography.RijndaelManaged(); _aesCipher.BlockSize = 128; _aesCipher.KeySize = keySizeInBits; // 128, 192, 256 _aesCipher.Mode = CipherMode.ECB; _aesCipher.Padding = PaddingMode.None; byte[] iv = new byte[BLOCK_SIZE_IN_BYTES]; // all zeroes // Create an ENCRYPTOR, regardless whether doing decryption or encryption. // It is reflexive. _xform = _aesCipher.CreateEncryptor(_params.KeyBytes, iv); }
public static WinZipAesCrypto ReadFromStream(string password, int KeyStrengthInBits, Stream s) { // from http://www.winzip.com/aes_info.htm // // Size(bytes) Content // ----------------------------------- // Variable Salt value // 2 Password verification value // Variable Encrypted file data // 10 Authentication code // // ZipEntry.CompressedSize represents the size of all of those elements. // salt size varies with key length: // 128 bit key => 8 bytes salt // 192 bits => 12 bytes salt // 256 bits => 16 bytes salt WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits); int saltSizeInBytes = c._KeyStrengthInBytes / 2; c._Salt = new byte[saltSizeInBytes]; c._providedPv = new byte[2]; s.Read(c._Salt, 0, c._Salt.Length); s.Read(c._providedPv, 0, c._providedPv.Length); c.PasswordVerificationStored = (Int16)(c._providedPv[0] + c._providedPv[1] * 256); if (password != null) { c.PasswordVerificationGenerated = (Int16)(c.GeneratedPV[0] + c.GeneratedPV[1] * 256); if (c.PasswordVerificationGenerated != c.PasswordVerificationStored) { throw new BadPasswordException("bad password"); } } return(c); }
private void SetupCryptoForExtract(string password) { //if (password == null) return; if (_Encryption_FromZipFile == EncryptionAlgorithm.None) return; if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak) { if (password == null) throw new ZipException("Missing password."); this.ArchiveStream.Seek(this.FileDataPosition - 12, SeekOrigin.Begin); _zipCrypto_forExtract = ZipCrypto.ForRead(password, this); } else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 || _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256) { if (password == null) throw new ZipException("Missing password."); // If we already have a WinZipAesCrypto object in place, use it. // It can be set up in the ReadDirEntry(), or during a previous Extract. if (_aesCrypto_forExtract != null) { _aesCrypto_forExtract.Password = password; } else { int sizeOfSaltAndPv = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile); this.ArchiveStream.Seek(this.FileDataPosition - sizeOfSaltAndPv, SeekOrigin.Begin); int keystrength = GetKeyStrengthInBits(_Encryption_FromZipFile); _aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(password, keystrength, this.ArchiveStream); } } }
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) { int bytesRead = 0; // change for workitem 8098 ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream); bytesRead += 4; // Return false if this is not a local file header signature. if (ZipEntry.IsNotValidSig(signature)) { // Getting "not a ZipEntry signature" is not always wrong or an error. // This will happen after the last entry in a zipfile. In that case, we // expect to read : // a ZipDirEntry signature (if a non-empty zip file) or // a ZipConstants.EndOfCentralDirectorySignature. // // Anything else is a surprise. ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) { throw new BadReadException(String.Format(CultureInfo.InvariantCulture, " Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position)); } return(false); } byte[] block = new byte[26]; int n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != block.Length) { return(false); } bytesRead += n; int i = 0; ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256); ze._BitField = (Int16)(block[i++] + block[i++] * 256); ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; // transform the time data into something usable (a DateTime) ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); ze._timestamp |= ZipEntryTimestamp.DOS; if ((ze._BitField & 0x01) == 0x01) { ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field ze._sourceIsEncrypted = true; } // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and // CRC values are not true values; the true values will follow the entry data. // But, regardless of the status of bit 3 in the bitfield, the slots for // the three amigos may contain marker values for ZIP64. So we must read them. { ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); if ((uint)ze._CompressedSize == 0xFFFFFFFF || (uint)ze._UncompressedSize == 0xFFFFFFFF) { ze._InputUsesZip64 = true; } } Int16 filenameLength = (short)(block[i++] + block[i++] * 256); Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); block = new byte[filenameLength]; n = ze.ArchiveStream.Read(block, 0, block.Length); bytesRead += n; // if the UTF8 bit is set for this entry, override the // encoding the application requested. if ((ze._BitField & 0x0800) == 0x0800) { // workitem 12744 ze.AlternateEncoding = System.Text.Encoding.UTF8; ze.AlternateEncodingUsage = ZipOption.Always; } // need to use this form of GetString() for .NET CF ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length); // workitem 6898 if (ze._FileNameInArchive.EndsWith("/", StringComparison.Ordinal)) { ze.MarkAsDirectory(); } bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength); ze._LengthOfTrailer = 0; // workitem 6607 - don't read for directories // actually get the compressed size and CRC if necessary if (!ze._FileNameInArchive.EndsWith("/", StringComparison.Ordinal) && (ze._BitField & 0x0008) == 0x0008) { // This descriptor exists only if bit 3 of the general // purpose bit flag is set (see below). It is byte aligned // and immediately follows the last byte of compressed data, // as well as any encryption trailer, as with AES. // This descriptor is used only when it was not possible to // seek in the output .ZIP file, e.g., when the output .ZIP file // was standard output or a non-seekable device. For ZIP64(tm) format // archives, the compressed and uncompressed sizes are 8 bytes each. // workitem 8098: ok (restore) long posn = ze.ArchiveStream.Position; // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and // a consistent data record after that. To be consistent, the data record must // indicate the length of the entry data. bool wantMore = true; long SizeOfDataRead = 0; int tries = 0; while (wantMore) { tries++; // We call the FindSignature shared routine to find the specified signature // in the already-opened zip archive, starting from the current cursor // position in that filestream. If we cannot find the signature, then the // routine returns -1, and the ReadHeader() method returns false, // indicating we cannot read a legal entry header. If we have found it, // then the FindSignature() method returns the number of bytes in the // stream we had to seek forward, to find the sig. We need this to // determine if the zip entry is valid, later. long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); if (d == -1) { return(false); } // total size of data read (through all loops of this). SizeOfDataRead += d; if (ze._InputUsesZip64) { // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) block = new byte[20]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 20) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._UncompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes } else { // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) block = new byte[12]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 12) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes } wantMore = (SizeOfDataRead != ze._CompressedSize); if (wantMore) { // Seek back to un-read the last 12 bytes - maybe THEY contain // the ZipEntryDataDescriptorSignature. // (12 bytes for the CRC, Comp and Uncomp size.) ze.ArchiveStream.Seek(-12, SeekOrigin.Current); // Adjust the size to account for the false signature read in // FindSignature(). SizeOfDataRead += 4; } } // seek back to previous position, to prepare to read file data // workitem 8098: ok (restore) ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); } ze._CompressedFileDataSize = ze._CompressedSize; // bit 0 set indicates that some kind of encryption is in use if ((ze._BitField & 0x01) == 0x01) { if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 || ze.Encryption == EncryptionAlgorithm.WinZipAes256) { int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile); // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128. ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream); bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes) // according to WinZip, the CompressedSize includes the AES Crypto framing data. ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata; ze._LengthOfTrailer += 10; // MAC } else { // read in the header data for "weak" encryption ze._WeakEncryptionHeader = new byte[12]; bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); // decrease the filedata size by 12 bytes ze._CompressedFileDataSize -= 12; } } // Remember the size of the blob for this entry. // We also have the starting position in the stream for this entry. ze._LengthOfHeader = bytesRead; // We've read in the regular entry header, the extra field, and any // encryption header. The pointer in the file is now at the start of the // filedata, which is potentially compressed and encrypted. Just ahead in // the file, there are _CompressedFileDataSize bytes of data, followed by // potentially a non-zero length trailer, consisting of optionally, some // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24 // bytes). return(true); }