Click here to Skip to main content
15,892,746 members
Articles / Web Development / ASP.NET

Easy String Compression and Encryption

Rate me:
Please Sign up or sign in to vote.
4.23/5 (22 votes)
1 Jul 2008CPOL3 min read 103K   4.5K   36  
Class module to easily de-/compress and en-/decrypt big strings
'String Compression Class
Public Class clsCompressedString

  'Only strings, no byte arrays are held within the class instance.
  'That avoids unnecessary conversions when requesting data more than
  'once from the class instance, without giving any new input.
  Private _UnCompressed As String = String.Empty
  Private _Compressed As String = String.Empty
  Private _TextEncoding As System.Text.Encoding = System.Text.Encoding.UTF8
  Private _PrefixForCompressedString As String = String.Empty
  Private _SuffixForCompressedString As String = String.Empty
  Private _Passphrase As String = String.Empty
  Private _CompressedGiven As Boolean = False

  Public Enum InputDataTypeClass
    Compressed = 1
    UnCompressed = 2
  End Enum

  Public Property UnCompressed() As String
    Get
      Return Me._UnCompressed
    End Get
    Set(ByVal value As String)
      Me._UnCompressed = value
      'Remember that uncompressed data was initially given
      Me._CompressedGiven = False
      'With the uncompressed data set we can start compression right away
      Me.Compress()
    End Set
  End Property

  Public Property Compressed() As String
    Get
      'If required, add prefix and/or suffix to the compressed string before returning it
      Dim Result As String = Me._Compressed
      If Result.Length > 0 Then
        If Me._PrefixForCompressedString.Length > 0 Then Result = Me._PrefixForCompressedString & Result
        If Me._SuffixForCompressedString.Length > 0 Then Result = Result & Me._SuffixForCompressedString
      End If
      Return Result
    End Get
    Set(ByVal value As String)
      'If required, remove prefix and/or suffix from the given compressed string
      Dim Result As String = value
      If Result.Length > 0 And Result.Length > (Me._PrefixForCompressedString.Length + Me._SuffixForCompressedString.Length) Then
        If Me._PrefixForCompressedString.Length > 0 Then
          Result = value.Substring(Me._PrefixForCompressedString.Length, Result.Length - Me._PrefixForCompressedString.Length)
        End If
        If Me._SuffixForCompressedString.Length > 0 Then
          Result = Result.Substring(0, Result.Length - Me._SuffixForCompressedString.Length)
        End If
      End If
      Me._Compressed = Result
      'Remember that compressed data was initially given
      Me._CompressedGiven = True
      'With the compressed data set we can start decompression right away
      Me.Decompress()
    End Set
  End Property

  Public Property TextEncoding() As System.Text.Encoding
    Get
      Return Me._TextEncoding
    End Get
    Set(ByVal value As System.Text.Encoding)
      Me._TextEncoding = value
    End Set
  End Property

  Public ReadOnly Property UnCompressed_Size() As Long
    Get
      Return Me._UnCompressed.Length
    End Get
  End Property

  Public ReadOnly Property Compressed_Size() As Long
    Get
      Return Me._Compressed.Length
    End Get
  End Property

  Public ReadOnly Property Compression_Ratio() As Double
    Get
      Dim Result As Double = 0
      If Me._Compressed.Length > 0 And Me._UnCompressed.Length > 0 Then
        Result = Me._Compressed.Length / Me._UnCompressed.Length
        Result = Math.Round(Result, 2, MidpointRounding.AwayFromZero)
      End If
      Return Result
    End Get
  End Property

  Public Property PrefixForCompressedString() As String
    Get
      Return Me._PrefixForCompressedString
    End Get
    Set(ByVal value As String)
      Me._PrefixForCompressedString = value
    End Set
  End Property

  Public Property SuffixForCompressedString() As String
    Get
      Return Me._SuffixForCompressedString
    End Get
    Set(ByVal value As String)
      Me._SuffixForCompressedString = value
    End Set
  End Property

  Public Property Passphrase() As String
    Get
      Return Me._Passphrase
    End Get
    Set(ByVal value As String)
      Me._Passphrase = value
      'With a new passphrase we possibly need to do a de-/compress,
      'if any data is available - depending on the desired direction.
      If Me._CompressedGiven Then
        If Me._Compressed.Length > 0 Then
          Me.Decompress()
        Else
          Me._UnCompressed = String.Empty
        End If
      Else
        If Me._UnCompressed.Length > 0 Then
          Me.Compress()
        Else
          Me._Compressed = String.Empty
        End If
      End If
    End Set
  End Property

  'No Parameters given
  Public Sub New()

  End Sub

  'Only TextEncoding given
  Public Sub New(ByVal TextEncoding As System.Text.Encoding)

    Me._TextEncoding = TextEncoding

  End Sub

  'Direct Compression/Decompression
  Public Sub New(ByVal TextEncoding As System.Text.Encoding, ByVal InputString As String, ByVal InputDataType As InputDataTypeClass, Optional ByVal Passphrase As String = "", Optional ByVal PrefixForCompressedString As String = "", Optional ByVal SuffixForCompressedString As String = "")

    Me._TextEncoding = TextEncoding
    Me._PrefixForCompressedString = PrefixForCompressedString
    Me._SuffixForCompressedString = SuffixForCompressedString
    Me._Passphrase = Passphrase

    Select Case InputDataType
      Case InputDataTypeClass.UnCompressed
        Me._UnCompressed = InputString
        Me.Compress()
      Case InputDataTypeClass.Compressed
        Dim Result As String = InputString
        If Result.Length > 0 And Result.Length > (Me._PrefixForCompressedString.Length + Me._SuffixForCompressedString.Length) Then
          If Me._PrefixForCompressedString.Length > 0 Then
            Result = InputString.Substring(Me._PrefixForCompressedString.Length, Result.Length - Me._PrefixForCompressedString.Length)
          End If
          If Me._SuffixForCompressedString.Length > 0 Then
            Result = Result.Substring(0, Result.Length - Me._SuffixForCompressedString.Length)
          End If
        End If
        Me._Compressed = Result
        Me._CompressedGiven = True
        Me.Decompress()
    End Select

  End Sub

  Private Sub Compress()

    If Me._UnCompressed.Length = 0 Then
      Me._Compressed = String.Empty
      Exit Sub
    End If

    Dim Result As String = String.Empty

    Try

      'Convert the uncompressed string into a byte array
      Dim UnZippedData As Byte() = Me._TextEncoding.GetBytes(Me._UnCompressed)

      'Compress the byte array
      Dim MS As New System.IO.MemoryStream()
      Dim GZip As New System.IO.Compression.GZipStream(MS, System.IO.Compression.CompressionMode.Compress)
      GZip.Write(UnZippedData, 0, UnZippedData.Length)
      'Don't FLUSH here - it possibly leads to data loss!
      GZip.Close()

      Dim ZippedData As Byte()

      'Encrypt the compressed byte array, if required
      If Me._Passphrase.Length > 0 Then
        ZippedData = Encrypt(MS.ToArray())
      Else
        ZippedData = MS.ToArray()
      End If

      'Convert the compressed byte array back to a string
      Result = System.Convert.ToBase64String(ZippedData)

      MS.Close()
      GZip.Dispose()
      MS.Dispose()

    Catch ex As Exception
      'Keep quiet - in case of an exception an empty string is returned
    Finally
      Me._Compressed = Result
    End Try

  End Sub

  Private Sub Decompress()

    If Me._Compressed.Length = 0 Then
      Me._UnCompressed = String.Empty
      Exit Sub
    End If

    Dim Result As String = String.Empty

    Try
      Dim ZippedData As Byte()

      'Convert the compressed string into a byte array and decrypt the array if required
      If Me._Passphrase.Length > 0 Then
        ZippedData = Decrypt(System.Convert.FromBase64String(Me._Compressed))
      Else
        ZippedData = System.Convert.FromBase64String(Me._Compressed)
      End If

      'Decompress the byte array
      Dim objMemStream As New IO.MemoryStream(ZippedData)
      Dim objGZipStream As New System.IO.Compression.GZipStream(objMemStream, System.IO.Compression.CompressionMode.Decompress)
      Dim sizeBytes(3) As Byte

      objMemStream.Position = objMemStream.Length - 5
      objMemStream.Read(sizeBytes, 0, 4)

      Dim iOutputSize As Integer = BitConverter.ToInt32(sizeBytes, 0)

      objMemStream.Position = 0

      Dim UnZippedData(iOutputSize - 1) As Byte

      objGZipStream.Read(UnZippedData, 0, iOutputSize)

      objGZipStream.Dispose()
      objMemStream.Dispose()

      'Convert the decompressed byte array back to a string
      Result = Me._TextEncoding.GetString(UnZippedData)

    Catch ex As Exception

    Finally
      Me._UnCompressed = Result
    End Try

  End Sub

  Private Function Encrypt(ByVal PlainData As Byte()) As Byte()

    Dim Result As Byte()

    Try

      Dim Enc As New System.Security.Cryptography.RijndaelManaged
      Enc.KeySize = 256
      Enc.Key = Me.Encryption_Key()
      Enc.IV = Me.Encryption_IV()

      Dim memoryStream As New IO.MemoryStream
      Dim cryptoStream As Security.Cryptography.CryptoStream
      cryptoStream = New Security.Cryptography.CryptoStream(memoryStream, Enc.CreateEncryptor, Security.Cryptography.CryptoStreamMode.Write)
      cryptoStream.Write(PlainData, 0, PlainData.Length)
      cryptoStream.FlushFinalBlock()
      Result = memoryStream.ToArray()
      cryptoStream.Close()
      memoryStream.Close()
      cryptoStream.Dispose()
      memoryStream.Dispose()

    Catch ex As Exception
      Result = Nothing
    End Try

    Return Result

  End Function

  Private Function Decrypt(ByVal EncData As Byte()) As Byte()

    Dim Result As Byte()

    Try
      Dim Enc As New System.Security.Cryptography.RijndaelManaged
      Enc.KeySize = 256
      Enc.Key = Me.Encryption_Key()
      Enc.IV = Me.Encryption_IV()

      Dim memoryStream As New IO.MemoryStream(EncData)
      Dim cryptoStream As Security.Cryptography.CryptoStream
      cryptoStream = New Security.Cryptography.CryptoStream(memoryStream, Enc.CreateDecryptor, Security.Cryptography.CryptoStreamMode.Read)

      Dim TempDecryptArr As Byte()
      ReDim TempDecryptArr(EncData.Length)
      Dim decryptedByteCount As Integer
      decryptedByteCount = cryptoStream.Read(TempDecryptArr, 0, EncData.Length)

      cryptoStream.Close()
      memoryStream.Close()
      cryptoStream.Dispose()
      memoryStream.Dispose()

      ReDim Result(decryptedByteCount)
      Array.Copy(TempDecryptArr, Result, decryptedByteCount)
    Catch ex As Exception
      Result = Nothing
    End Try

    Return Result

  End Function

  Private Function Encryption_Key() As Byte()

    'Generate a byte array of required length as the encryption key.
    'A SHA256 hash of the passphrase has just the required length. It is used twice in a manner of self-salting.
    Dim SHA256 As New System.Security.Cryptography.SHA256Managed
    Dim L1 As String = System.Convert.ToBase64String(SHA256.ComputeHash(Me._TextEncoding.GetBytes(Me._Passphrase)))
    Dim L2 As String = Me.Passphrase & L1
    Dim Result As Byte() = SHA256.ComputeHash(Me._TextEncoding.GetBytes(L2))
    Return Result

  End Function

  Private Function Encryption_IV() As Byte()

    'Generate a byte array of required length as the iv.
    'A MD5 hash of the passphrase has just the required length. It is used twice in a manner of self-salting.
    Dim MD5 As New System.Security.Cryptography.MD5CryptoServiceProvider
    Dim L1 As String = System.Convert.ToBase64String(MD5.ComputeHash(Me._TextEncoding.GetBytes(Me._Passphrase)))
    Dim L2 As String = Me.Passphrase & L1
    Dim Result As Byte() = MD5.ComputeHash(Me._TextEncoding.GetBytes(L2))
    Return Result

  End Function

End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions