internal void WriteSecurityMetadata(Stream outstream)
{
if (Encryption == EncryptionAlgorithm.None)
return;
string pwd = this._Password;
// special handling for source == ZipFile.
// Want to support the case where we re-stream an encrypted entry. This will involve,
// at runtime, reading, decrypting, and decompressing from the original zip file, then
// compressing, encrypting, and writing to the output zip file.
// If that's what we're doing, and the password hasn't been set on the entry,
// we use the container (ZipFile/ZipOutputStream) password to decrypt.
// This test here says to use the container password to re-encrypt, as well,
// with that password, if the entry password is null.
if (this._Source == ZipEntrySource.ZipFile && pwd == null)
pwd = this._container.Password;
if (pwd == null)
{
_zipCrypto_forWrite = null;
#if AESCRYPTO
_aesCrypto_forWrite = null;
#endif
return;
}
TraceWriteLine("WriteSecurityMetadata: e({0}) crypto({1}) pw({2})",
FileName, Encryption.ToString(), pwd);
if (Encryption == EncryptionAlgorithm.PkzipWeak)
{
// If PKZip (weak) encryption is in use, then the encrypted entry data
// is preceded by 12-byte "encryption header" for the entry.
_zipCrypto_forWrite = ZipCrypto.ForWrite(pwd);
// generate the random 12-byte header:
var rnd = new System.Random();
byte[] encryptionHeader = new byte[12];
rnd.NextBytes(encryptionHeader);
// workitem 8271
if ((this._BitField & 0x0008) == 0x0008)
{
// In the case that bit 3 of the general purpose bit flag is set to
// indicate the presence of 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.
// This was discovered this by analysis of the Crypt.c source file in the
// InfoZip library
// http://www.info-zip.org/pub/infozip/
// Also, winzip insists on this!
_TimeBlob = Crisis.Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified);
encryptionHeader[11] = (byte)((this._TimeBlob >> 8) & 0xff);
}
else
{
// When bit 3 is not set, the CRC value is required before
// encryption of the file data begins. In this case there is no way
// around it: must read the stream in its entirety to compute the
// actual CRC before proceeding.
FigureCrc32();
encryptionHeader[11] = (byte)((this._Crc32 >> 24) & 0xff);
}
// Encrypt the random header, INCLUDING the final byte which is either
// the high-order byte of the CRC32, or the high-order byte of the
// _TimeBlob. Must do this BEFORE encrypting 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.
byte[] cipherText = _zipCrypto_forWrite.EncryptMessage(encryptionHeader, encryptionHeader.Length);
// Write the ciphered bonafide encryption header.
outstream.Write(cipherText, 0, cipherText.Length);
_LengthOfHeader += cipherText.Length; // 12 bytes
}
#if AESCRYPTO
else if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
Encryption == EncryptionAlgorithm.WinZipAes256)
{
// If WinZip AES encryption is in use, then the encrypted entry data is
// preceded by a variable-sized Salt and a 2-byte "password
// verification" value for the entry.
int keystrength = GetKeyStrengthInBits(Encryption);
_aesCrypto_forWrite = WinZipAesCrypto.Generate(pwd, keystrength);
outstream.Write(_aesCrypto_forWrite.Salt, 0, _aesCrypto_forWrite._Salt.Length);
outstream.Write(_aesCrypto_forWrite.GeneratedPV, 0, _aesCrypto_forWrite.GeneratedPV.Length);
_LengthOfHeader += _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length;
TraceWriteLine("WriteSecurityMetadata: AES e({0}) keybits({1}) _LOH({2})",
FileName, keystrength, _LengthOfHeader);
}
#endif
}