Imports System.Security.Cryptography
Imports System.IO
Imports System.Text
Imports System.Web

'SymmCrypto is a wrapper of System.Security.Cryptography.SymmetricAlgorithm classes
'and simplifies the interface. It supports customized SymmetricAlgorithm as well.
'Original Code from Frank Fang
'Revised by Jerome Howard to remove Bad Data errors, create seperate CryptoIV and
'use the maximum legal keysize for each encryption algorithm

Namespace Training2.Business

    Public Class Crypto
        '256 Bit IV Key that is truncated when a smaller keys are required
        Private bytIV() As Byte = _
        {78, 90, 23, 7, 54, 109, 34, 231, 90, 66, 109, 185, 228, 143, 89, 77, 190, 89, 103, 148, 54, 4, 98, 67, 243, 162, 68, 201, 73, 59, 184, 52 }

        'Supported .Net intrinsic SymmetricAlgorithm classes.
        Public Enum Providers
            DES
            RC2
            Rijndael
        End Enum

        Private _CryptoService As SymmetricAlgorithm

        'Constructor for using an intrinsic .Net SymmetricAlgorithm class.
        Public Sub New(ByVal NetSelected As Providers)
            Select Case NetSelected
                Case Providers.DES
                    _CryptoService = New DESCryptoServiceProvider
                Case Providers.RC2
                    _CryptoService = New RC2CryptoServiceProvider
                Case Providers.Rijndael
                    _CryptoService = New RijndaelManaged
            End Select
        End Sub

        'Constructor for using a customized SymmetricAlgorithm class.
        Public Sub New(ByVal ServiceProvider As SymmetricAlgorithm)
            _CryptoService = ServiceProvider
        End Sub

        'Depending on the legal key size limitations of a specific CryptoService provider
        'and length of the private key provided, padding the secret key with a character
        'or triming it to meet the legal size of the algorithm.
        Private Function GetLegalKey(ByVal Key As String) As Byte()
            'key sizes are in bits
            Dim sTemp As String
            Dim dValue As Integer
            If (_CryptoService.LegalKeySizes.Length > 0) Then
                Dim maxSize As Integer = _CryptoService.LegalKeySizes(0).MaxSize
                dValue = CInt(maxSize / 8)
                If Key.Length * 8 > maxSize Then
                    sTemp = Key.Substring(0, dValue)
                Else
                    Dim moreSize As Integer = _CryptoService.LegalKeySizes(0).MinSize
                    Do While (Key.Length * 8 > moreSize)
                        moreSize += _CryptoService.LegalKeySizes(0).SkipSize
                    Loop
                    sTemp = Key.PadRight(dValue, "X".Chars(0))
                End If
            Else
                sTemp = Key
            End If

            'Ensure that the IV Block size is also correct for the specific CryptoService provider
            If (_CryptoService.LegalBlockSizes.Length > 0) Then
                Dim maxSize As Integer = _CryptoService.LegalBlockSizes(0).MaxSize
                dValue = CInt(maxSize / 8)
                ReDim Preserve bytIV(sTemp.Length - 1)
                If sTemp.Length * 8 > maxSize Then
                    ReDim Preserve bytIV(dValue - 1)
                End If
            End If
            'convert the secret key to byte array
            Return ASCIIEncoding.ASCII.GetBytes(sTemp)
        End Function

        Public Function Encrypt(ByVal Source As String, ByVal Key As String, Optional ByVal Encoded As Boolean = True) As String
            Return Me.Encrypt(Source, Key, System.Text.Encoding.ASCII, Encoded)
        End Function

        Public Function Encrypt(ByVal Source As String, ByVal Key As String, ByVal Encoding As System.Text.Encoding, Optional ByVal Encoded As Boolean = True) As String
            If Encoded Then
                Source = System.Web.HttpUtility.UrlEncode(Source)
            End If
            Dim bytIn As Byte()
            If Encoding.EncodingName = System.Text.Encoding.ASCII.EncodingName Then
                bytIn = System.Text.ASCIIEncoding.ASCII.GetBytes(Source)
            ElseIf Encoding.EncodingName = System.Text.Encoding.Unicode.EncodingName Then
                bytIn = System.Text.UnicodeEncoding.Unicode.GetBytes(Source)
            Else
                bytIn = System.Text.ASCIIEncoding.ASCII.GetBytes(Source)
            End If
            '= System.Text.ASCIIEncoding.ASCII.GetBytes(Source)
            Dim ms As MemoryStream = New MemoryStream

            'set the keys
            _CryptoService.Key = GetLegalKey(Key)
            _CryptoService.IV = bytIV

            'create an Encryptor from the Provider Service instance
            Dim encrypto As ICryptoTransform = _CryptoService.CreateEncryptor()

            'create Crypto Stream that transforms a stream using the encryption
            Dim cs As CryptoStream = New CryptoStream(ms, encrypto, CryptoStreamMode.Write)

            'write out encrypted content into MemoryStream
            cs.Write(bytIn, 0, bytIn.Length)
            cs.FlushFinalBlock()
            cs.Close()
            Dim bytOut() As Byte = ms.ToArray()
            ms.Close()

            Return Convert.ToBase64String(bytOut) 'convert into Base64 so that the result can be used in xml
        End Function
        Public Function Decrypt(ByVal Source As String, ByVal Key As String, Optional ByVal Encoded As Boolean = True) As String
            Return Me.Decrypt(Source, Key, System.Text.Encoding.ASCII, Encoded)
        End Function

        Public Function Decrypt(ByVal Source As String, ByVal Key As String, ByVal Encoding As System.Text.Encoding, Optional ByVal Encoded As Boolean = True) As String
            'convert from Base64 to binary
            Dim bytIn As Byte() = System.Convert.FromBase64String(Source)
            Dim ms As MemoryStream = New MemoryStream(bytIn)

            Dim bytKey() As Byte = GetLegalKey(Key)
            Dim bytTemp(bytIn.Length) As Byte

            'set the private key
            _CryptoService.Key = bytKey
            _CryptoService.IV = bytIV

            'create a Decryptor from the Provider Service instance
            Dim encrypto As ICryptoTransform = _CryptoService.CreateDecryptor()

            'create Crypto Stream that transforms a stream using the decryption
            Dim cs As CryptoStream = New CryptoStream(ms, encrypto, CryptoStreamMode.Read)
            Dim output As String = ""
            Try
                'read out the result from the Crypto Stream
                Dim sr As StreamReader
                If Encoding.EncodingName = System.Text.Encoding.ASCII.EncodingName Then
                    sr = New StreamReader(cs)
                ElseIf Encoding.EncodingName = System.Text.Encoding.Unicode.EncodingName Then
                    sr = New StreamReader(cs, System.Text.UnicodeEncoding.Unicode)
                Else
                    sr = New StreamReader(cs)
                End If
                output = sr.ReadToEnd
                sr.Close()
                ms.Close()
                cs.Close()
            Catch ex As Exception
            End Try
            If Encoded Then
                output = System.Web.HttpUtility.UrlDecode(output)
            End If
            Return output
        End Function

    End Class

End Namespace