Xceed .NET Libraries Documentation
Unzipping encrypted items that use 'compatible' encryption
Welcome to Xceed .NET, .NET Standard and Xamarin Libraries! > Task-Based Help > Zip and streaming capabilities > Unzipping > Unzipping encrypted items that use 'compatible' encryption

When decrypting data using Compatible encryption, the password verification isn't 100% accurate.

It is possible that an invalid password will not be detected. In that case, decryption will process but the decrypted data will be garbage. This is an unfortunate design flaw of the encryption algorithm.

This behavior only applies to items encrypted with the Compatible encryption method.

In the context of unzipping, this will always result in a decompression failure or a checksum failure so no incorrect data will ever be delivered. However, special care needs to be taken when handling exceptions.

This example shows how this situation can be handled. It examines the exceptions thrown by decompression as well as what kind of items being processed to distinguish between an actual data error and a probable invalid password.

If all the encrypted items in an archive use the same password, this example is not absolutely necessary as any error can be treated in the same way. For example, many third party Zip tools will say something like: "Data error in item 'X'. Wrong password?".

using System;

using Xceed.FileSystem;
using Xceed.Zip;

namespace DocumentationExamples.Zip
{
  class CompatibleEncryption
  {
    private void OnItemProgression( object sender, ItemProgressionEventArgs e )
    {
      /* We're about to process a new item */

      // Reset the password index
      this.m_passwordIndex = 0;
    }

    private void OnItemException( object sender, ItemExceptionEventArgs e )
    {
      bool badPassword = false;

      // Get the archive object we passed through the user data
      ZipArchive zip = ( ZipArchive ) e.UserData;

      /* We will try to ascertain if we have an invalid password */

      InvalidDecryptionPasswordException invalidDecryptionPasswordException;
      FileSystemIOException fileSystemIOException;

      invalidDecryptionPasswordException = e.Exception as InvalidDecryptionPasswordException;
      fileSystemIOException = e.Exception as FileSystemIOException;

      // If the exception says that a bad password was supplied
      if( invalidDecryptionPasswordException != null )
      {
        badPassword = true;
      }
      // If we had an I/O error during decryption
      else if( fileSystemIOException != null )
      {
        ZippedFile zippedFile = e.CurrentItem as ZippedFile;

        // If we were reading from a zipped file encrypted in the 'compatible' method
        if( zippedFile != null && zippedFile.Encrypted && 
            zippedFile.EncryptionMethod.Equals( EncryptionMethod.Compatible ) )
        {
          /* It's possible the I/O error occurred because the password is invalid.
           The way the 'compatible' encryption is designed doesn't provide for 100%
           accurate bad password detection, unfortunately. */
          badPassword = true;
        }
      }

      // If we had a bad password
      if( badPassword )
      {
        // If we haven't gone through our password list
        if( this.m_passwordIndex < this.m_passwords.Length )
        {
          // Set the current password and move the index to the next password
          zip.DefaultDecryptionPassword = this.m_passwords[ this.m_passwordIndex++ ];

          // Retry unzipping the file
          e.Action = ItemExceptionAction.Retry;
        }
        else
        {
          // Skip the file
          e.Action = ItemExceptionAction.Ignore;
        }
      }
    }

    public void Example()
    {
      AbstractFile zipFile = new DiskFile( @"ZipFileWithEncryptedItems.zip" );
      AbstractFolder destinationFolder = new DiskFolder( @"Output" );

      ZipArchive zip = new ZipArchive( zipFile );

      // Set up the list of password possible for the items in this archive
      this.m_passwords = new string[]
      {
        "wrong password",
        "fkE%I-969%=6kei$[BbZ \"6Iq- =[",
        "}8{)zM#$k//O?t~=iG'Si{AF\"S~\'/8@1n",
      };

      ZipEvents events = new ZipEvents();

      // Subscribe to the events that will allow us to handle invalid passwords
      events.ItemProgression += new ItemProgressionEventHandler( OnItemProgression );
      events.ItemException += new ItemExceptionEventHandler( OnItemException );

      // Unzip the contents of the archive using the events object we set up
      zip.CopyFilesTo( events, zip, destinationFolder, true, true );
    }

    private string[] m_passwords;
    private int m_passwordIndex;
  }
}
Imports Microsoft.VisualBasic
Imports System

Imports Xceed.FileSystem
Imports Xceed.Zip

Namespace DocumentationExamples.Zip
  Friend Class CompatibleEncryption
    Private Sub OnItemProgression(ByVal sender As Object, ByVal e As ItemProgressionEventArgs)
      ' We're about to process a new item 

      ' Reset the password index
      Me.m_passwordIndex = 0
    End Sub

    Private Sub OnItemException(ByVal sender As Object, ByVal e As ItemExceptionEventArgs)
      Dim badPassword As Boolean = False

      ' Get the archive object we passed through the user data
      Dim zip As ZipArchive = CType(e.UserData, ZipArchive)

      ' We will try to ascertain if we have an invalid password 

      Dim invalidDecryptionPasswordException As InvalidDecryptionPasswordException
      Dim fileSystemIOException As FileSystemIOException

      invalidDecryptionPasswordException = TryCast(e.Exception, InvalidDecryptionPasswordException)
      fileSystemIOException = TryCast(e.Exception, FileSystemIOException)

      ' If the exception says that a bad password was supplied
      If invalidDecryptionPasswordException IsNot Nothing Then
        badPassword = True
      ' If we had an I/O error during decryption
      ElseIf fileSystemIOException IsNot Nothing Then
        Dim zippedFile As ZippedFile = TryCast(e.CurrentItem, ZippedFile)

        ' If we were reading from a zipped file encrypted in the 'compatible' method
        If zippedFile IsNot Nothing AndAlso zippedFile.Encrypted AndAlso zippedFile.EncryptionMethod.Equals(EncryptionMethod.Compatible) Then
'           It's possible the I/O error occurred because the password is invalid.
'           The way the 'compatible' encryption is designed doesn't provide for 100%
'           accurate bad password detection, unfortunately. 
          badPassword = True
        End If
      End If

      ' If we had a bad password
      If badPassword Then
        ' If we haven't gone through our password list
        If Me.m_passwordIndex < Me.m_passwords.Length Then
          ' Set the current password and move the index to the next password
          zip.DefaultDecryptionPassword = Me.m_passwords(Me.m_passwordIndex)
          Me.m_passwordIndex += 1

          ' Retry unzipping the file
          e.Action = ItemExceptionAction.Retry
        Else
          ' Skip the file
          e.Action = ItemExceptionAction.Ignore
        End If
      End If
    End Sub

    Public Sub Example()
      Dim zipFile As AbstractFile = New DiskFile("ZipFileWithEncryptedItems.zip")
      Dim destinationFolder As AbstractFolder = New DiskFolder("Output")

      Dim zip As New ZipArchive(zipFile)

      ' Set up the list of password possible for the items in this archive
      Me.m_passwords = New String() { "wrong password", "fkE%I-969%=6kei$[BbZ ""6Iq- =[", "}8{)zM#$k//O?t~=iG'Si{AF""S~'/8@1n" }

      Dim events As New ZipEvents()

      ' Subscribe to the events that will allow us to handle invalid passwords
      AddHandler events.ItemProgression, AddressOf OnItemProgression
      AddHandler events.ItemException, AddressOf OnItemException

      ' Unzip the contents of the archive using the events object we set up
      zip.CopyFilesTo(events, zip, destinationFolder, True, True)
    End Sub

    Private m_passwords() As String
    Private m_passwordIndex As Integer
  End Class
End Namespace