This feature introduces password support for ZIP archives, enabling encryption and decryption of archive entries. The proposal adds support for industry-standard WinZip AES encryption and support for legacy ZipCrypto method.
namespaceSystem.IO.Compression;// This enum is intended purely as a user-friendly way to specify the encryption method.publicenumEncryptionMethod{None=0,ZipCrypto=1,// or possibly LegacyZipCrypto to make it clear we don't recommend itAes128=2,Aes192=3,Aes256=4}publicpartialclassZipArchiveEntry{// ExistingpublicStreamOpen();publicStreamOpen(FileAccessaccess);publicTask<Stream>OpenAsync(CancellationTokencancellationToken=default);publicTask<Stream>OpenAsync(FileAccessaccess,CancellationTokencancellationToken=default);// New (decryption)// Opens an encrypted entry in read or update mode.// Throws InvalidDataException if the entry is not encrypted or the password is wrong.// Throws InvalidOperationException in Create mode (no existing entry to decrypt).publicStreamOpen(ReadOnlySpan<char>password);publicStreamOpen(FileAccessaccess,ReadOnlySpan<char>password);publicTask<Stream>OpenAsync(ReadOnlyMemory<char>password,CancellationTokencancellationToken=default);publicTask<Stream>OpenAsync(FileAccessaccess,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default);// New (encryption)// Opens a new entry for writing with encryption.// Only valid in Create mode; throws InvalidOperationException in Read mode and InvalidDataException in Update mode.// Throws ArgmentException if password is empty.// Throws PlatuformNotSupportedException for AES methods on browser platforms.publicStreamOpen(ReadOnlySpan<char>password,ZipEncryptionMethodencryptionMethod);publicStreamOpen(FileAccessaccess,ReadOnlySpan<char>password,ZipEncryptionMethodencryptionMethod);publicTask<Stream>OpenAsync(ReadOnlyMemory<char>password,ZipEncryptionMethodencryptionMethod,CancellationTokencancellationToken=default);publicTask<Stream>OpenAsync(FileAccessaccess,ReadOnlyMemory<char>password,ZipEncryptionMethodencryptionMethod,CancellationTokencancellationToken=default);}publicstaticclassZipFileExtensions{// ExtractToDirectory (file-based, sync) exsitingpublicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName){}publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,booloverwriteFiles){}publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding){}publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles){}// ExtractToDirectory (file-based, sync) new// Decrypts encrypted entries with the given password.// Throws InvalidDataException if any encrypted entry fails decryption.// If password is empty, falls back to passwordless extraction.publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,booloverwriteFiles,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles,ReadOnlySpan<char>password){}// ExtractToDirectory (file-based, async) exsitingpublicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,booloverwriteFiles,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles,CancellationTokencancellationToken=default){}// ExtractToDirectory (file-based, async) exsitingpublicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,booloverwriteFiles,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(stringsourceArchiveFileName,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}// ExtractToDirectory (stream-based, sync) existingpublicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName){}publicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,booloverwriteFiles){}publicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding){}publicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles){}// ExtractToDirectory (stream-based, sync) newpublicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,booloverwriteFiles,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles,ReadOnlySpan<char>password){}// ExtractToDirectory (stream-based, async) existingpublicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,booloverwriteFiles,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles,CancellationTokencancellationToken=default){}// ExtractToDirectory (stream-based, async) newpublicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,booloverwriteFiles,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(Streamsource,stringdestinationDirectoryName,Encoding?entryNameEncoding,booloverwriteFiles,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}// CreateEntryFromFile, sync, existingpublicstaticZipArchiveEntryCreateEntryFromFile(ZipArchivedestination,stringsourceFileName,stringentryName){}publicstaticZipArchiveEntryCreateEntryFromFile(ZipArchivedestination,stringsourceFileName,stringentryName,CompressionLevelcompressionLevel){}// CreateEntryFromFile, sync, newpublicstaticZipArchiveEntryCreateEntryFromFile(ZipArchivedestination,stringsourceFileName,stringentryName,ReadOnlySpan<char>password,EncryptionMethodencryption){}publicstaticZipArchiveEntryCreateEntryFromFile(ZipArchivedestination,stringsourceFileName,stringentryName,CompressionLevelcompressionLevel,ReadOnlySpan<char>password,EncryptionMethodencryption){}// CreateEntryFromFile, async, existingpublicstaticTask<ZipArchiveEntry>CreateEntryFromFileAsync(ZipArchivedestination,stringsourceFileName,stringentryName,CancellationTokencancellationToken=default);publicstaticTask<ZipArchiveEntry>CreateEntryFromFileAsync(ZipArchivedestination,stringsourceFileName,stringentryName,CompressionLevelcompressionLevel,CancellationTokencancellationToken=default);// CreateEntryFromFile, async, newpublicstaticTask<ZipArchiveEntry>CreateEntryFromFileAsync(ZipArchivedestination,stringsourceFileName,stringentryName,ReadOnlyMemory<char>password,EncryptionMethodencryption,CancellationTokencancellationToken=default){}publicstaticTask<ZipArchiveEntry>CreateEntryFromFileAsync(ZipArchivedestination,stringsourceFileName,stringentryName,CompressionLevelcompressionLevel,ReadOnlyMemory<char>password,EncryptionMethodencryption,CancellationTokencancellationToken=default){}// ExtractToFile, sync, existingpublicstaticvoidExtractToFile(thisZipArchiveEntrysource,stringdestinationFileName){}publicstaticvoidExtractToFile(thisZipArchiveEntrysource,stringdestinationFileName,booloverwrite){}// ExtractToFile, sync, newpublicstaticvoidExtractToFile(thisZipArchiveEntrysource,stringdestinationFileName,ReadOnlySpan<char>password){}publicstaticvoidExtractToFile(thisZipArchiveEntrysource,stringdestinationFileName,booloverwrite,ReadOnlySpan<char>password){}// ExtractToFile, async, existingpublicstaticTaskExtractToFileAsync(ZipArchiveEntrysource,stringdestinationFileName,CancellationTokencancellationToken=default);publicstaticTaskExtractToFileAsync(ZipArchiveEntrysource,stringdestinationFileName,booloverwrite,CancellationTokencancellationToken=default);// ExtractToFile, async, newpublicstaticTaskExtractToFileAsync(thisZipArchiveEntrysource,stringdestinationFileName,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToFileAsync(thisZipArchiveEntrysource,stringdestinationFileName,booloverwrite,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}// ExtractToDirectory (ZipArchive extension), sync, exisitngpublicstaticvoidExtractToDirectory(thisZipArchivesource,stringdestinationDirectoryName){}publicstaticvoidExtractToDirectory(thisZipArchivesource,stringdestinationDirectoryName,booloverwriteFiles){}// ExtractToDirectory (ZipArchive extension), sync, newpublicstaticvoidExtractToDirectory(thisZipArchivesource,stringdestinationDirectoryName,ReadOnlySpan<char>password){}publicstaticvoidExtractToDirectory(thisZipArchivesource,stringdestinationDirectoryName,booloverwriteFiles,ReadOnlySpan<char>password){}// ExtractToDirectory (ZipArchive extension), async, exisitngpublicstaticTaskExtractToDirectoryAsync(thisZipArchivesource,stringdestinationDirectoryName,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(thisZipArchivesource,stringdestinationDirectoryName,booloverwriteFiles,CancellationTokencancellationToken=default){}// ExtractToDirectory (ZipArchive extension), async, newpublicstaticTaskExtractToDirectoryAsync(thisZipArchivesource,stringdestinationDirectoryName,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}publicstaticTaskExtractToDirectoryAsync(thisZipArchivesource,stringdestinationDirectoryName,booloverwriteFiles,ReadOnlyMemory<char>password,CancellationTokencancellationToken=default){}}
ZIP Archive Encryption API Proposal
Background and Motivation
This feature introduces password support for ZIP archives, enabling encryption and decryption of archive entries. The proposal adds support for industry-standard WinZip AES encryption and support for legacy ZipCrypto method.
ReadOnlySpan<char>(sync) andReadOnlyMemory<char>(async) to enable safer handling of sensitive data.Platform Notes:
Archive Structure:
ExtractToDirectorycan only work with archives where all encrypted entries use the same password. Mixed password scenarios require entry-by-entry handling viaZipArchiveEntry.Open(password).API Proposal
Open Questions
Lax Password Handling: When an archive contains mixed plain and encrypted entries, and
ExtractToDirectory(password)is called with a single password:Open(password) on Unencrypted Entry: When calling
ZipArchiveEntry.Open(password)on an entry that is not encrypted:ArgumentException. Should this behavior be more lenient (e.g., just open the entry unencrypted)?API Usage
Creating a password-protected archive:
Extracting a password-protected archive:
Opening individual encrypted entries:
Async extraction with password:
Using char arrays for zero-copy password handling: