/// <summary> /// Constructor /// </summary> /// <param name="stream">The stream on which to perform the cryptographic transformation.</param> /// <param name="transform">Instance of ZipAESTransform</param> /// <param name="mode">Read or Write</param> public ZipAESStream(Stream stream, ZipAESTransform transform, CryptoStreamMode mode) : base(stream, transform, mode) { _stream = stream; _transform = transform; _slideBuffer = new byte[1024]; _blockAndAuth = CRYPTO_BLOCK_SIZE + AUTH_CODE_LENGTH; // mode: // CryptoStreamMode.Read means we read from "stream" and pass decrypted to our Read() method. // Write bypasses this stream and uses the Transform directly. if (mode != CryptoStreamMode.Read) { throw new Exception("ZipAESStream only for read"); } }
// File format for AES: // Size (bytes) Content // ------------ ------- // Variable Salt value // 2 Password verification value // Variable Encrypted file data // 10 Authentication code // // Value in the "compressed size" fields of the local file header and the central directory entry // is the total size of all the items listed above. In other words, it is the total size of the // salt value, password verification value, encrypted data, and authentication code. /// <summary> /// Initializes encryption keys based on given password. /// </summary> protected byte[] InitializeAESPassword(ZipEntry entry, string rawPassword) { var salt = new byte[entry.AESSaltLen]; // Salt needs to be cryptographically random, and unique per file if (_aesRnd == null) { _aesRnd = RandomNumberGenerator.Create(); } _aesRnd.GetBytes(salt); int blockSize = entry.AESKeySize / 8; // bits to bytes cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true); var headBytes = new byte[salt.Length + 2]; Array.Copy(salt, headBytes, salt.Length); Array.Copy(((ZipAESTransform)cryptoTransform_).PwdVerifier, 0, headBytes, headBytes.Length - 2, 2); return(headBytes); }
Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) { CryptoStream result = null; if ( (entry.Version < ZipConstants.VersionStrongEncryption) || (entry.Flags & (int)GeneralBitFlags.StrongEncryption) == 0) { PkzipClassicManaged classicManaged = new PkzipClassicManaged(); OnKeysRequired(entry.Name); if (HaveKeys == false) { throw new ZipException("No password available for encrypted stream"); } result = new CryptoStream(baseStream, classicManaged.CreateDecryptor(key, null), CryptoStreamMode.Read); CheckClassicPassword(result, entry); } else { #if !NET_1_1 && !NETCF_2_0 if (entry.Version == ZipConstants.VERSION_AES) { // OnKeysRequired(entry.Name); if (HaveKeys == false) { throw new ZipException("No password available for AES encrypted stream"); } int saltLen = entry.AESSaltLen; byte[] saltBytes = new byte[saltLen]; int saltIn = baseStream.Read(saltBytes, 0, saltLen); if (saltIn != saltLen) throw new ZipException("AES Salt expected " + saltLen + " got " + saltIn); // byte[] pwdVerifyRead = new byte[2]; baseStream.Read(pwdVerifyRead, 0, 2); int blockSize = entry.AESKeySize / 8; // bits to bytes ZipAESTransform decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false); byte[] pwdVerifyCalc = decryptor.PwdVerifier; if (pwdVerifyCalc[0] != pwdVerifyRead[0] || pwdVerifyCalc[1] != pwdVerifyRead[1]) throw new Exception("Invalid password for AES"); result = new ZipAESStream(baseStream, decryptor, CryptoStreamMode.Read); } else #endif { throw new ZipException("Decryption method not supported"); } } return result; }