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