public static ZipCrypto ForRead(string password, ZipEntry e)
{
System.IO.Stream s = e._archiveStream;
e._WeakEncryptionHeader = new byte[12];
byte[] eh = e._WeakEncryptionHeader;
ZipCrypto z = new ZipCrypto();
if (password == null)
throw new BadPasswordException("This entry requires a password.");
z.InitCipher(password);
ZipEntry.ReadWeakEncryptionHeader(s, eh);
// Decrypt the header. This has a side effect of "further initializing the
// encryption keys" in the traditional zip encryption.
byte[] DecryptedHeader = z.DecryptMessage(eh, eh.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)((e._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' or a 'data
// descriptor' (signature 0x08074b50), the last byte of the decrypted
// header is sometimes compared with the high-order byte of the
// lastmodified time, rather than the high-order byte of the CRC, to
// verify the password.
//
// This is not documented in the PKWare Appnote.txt. It was
// discovered this by analysis of the Crypt.c source file in the
// InfoZip library http://www.info-zip.org/pub/infozip/
//
// The reason for this is that the CRC for a file cannot be known
// until the entire contents of the file have been streamed. This
// means a tool would have to read the file content TWICE in its
// entirety in order to perform PKZIP encryption - once to compute
// the CRC, and again to actually encrypt.
//
// This is so important for performance that using the timeblob as
// the verification should be the standard practice for DotNetZip
// when using PKZIP encryption. This implies that bit 3 must be
// set. The downside is that some tools still cannot cope with ZIP
// files that use bit 3. Therefore, DotNetZip DOES NOT force bit 3
// when PKZIP encryption is in use, and instead, reads the stream
// twice.
//
if ((e._BitField & 0x0008) != 0x0008)
{
throw new BadPasswordException("The password did not match.");
}
else if (DecryptedHeader[11] != (byte)((e._TimeBlob >> 8) & 0xff))
{
throw new BadPasswordException("The password did not match.");
}
// We have a good password.
}
else
{
// A-OK
}
return z;
}