Option Explicit On
Option Strict On
Imports System
Imports System.IO
Imports System.Text
Imports System.Security
Imports System.Security.Cryptography
Imports System.Runtime.InteropServices
Imports SSCrypto
Imports SSCommonLib.Text
Namespace Security
Public Class Security
#Region "Const & Variable"
Public Const COMP_KEY As String = "SSViewer@COMP"
Public Const COMP_KEY_HASH As String = "SHA1"
Public Const COMP_KEY_ITERATION As Integer = 2
Public Const COMP_IV As String = "@COMP@20112031"
#End Region
#Region "SHA1 Hash"
''' <summary>
''' Generates a hash for the given plain text value and returns a base64-encoded result.
''' </summary>
''' <param name="plainText">Plaintext value to be hashed.The function does not check whether this parameter is null.</param>
''' <param name="saltBytes">Salt bytes. This parameter can be null, in which case a random salt value will be generated.</param>
''' <returns>Hash value formatted as a base64-encoded string.</returns>
''' <remarks></remarks>
Public Shared Function ComputeSHA1Hash(ByVal plainText As String, Optional ByVal saltBytes() As Byte = Nothing) As String
If (saltBytes Is Nothing) Then
' Define min and max salt sizes.
Dim minSaltSize As Integer
Dim maxSaltSize As Integer
minSaltSize = 4
maxSaltSize = 8
' Generate a random number for the size of the salt.
Dim random As Random
random = New Random()
Dim saltSize As Integer
saltSize = random.Next(minSaltSize, maxSaltSize)
' Allocate a byte array, which will hold the salt.
saltBytes = New Byte(saltSize - 1) {}
' Initialize a random number generator.
Dim rng As RNGCryptoServiceProvider
rng = New RNGCryptoServiceProvider()
' Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes)
End If
' Convert plain text into a byte array.
Dim plainTextBytes As Byte()
plainTextBytes = Encoding.ASCII.GetBytes(plainText)
' Allocate array, which will hold plain text and salt.
Dim plainTextWithSaltBytes() As Byte = New Byte(plainTextBytes.Length + saltBytes.Length - 1) {}
Dim I As Integer
' Copy plain text bytes into resulting array.
For I = 0 To plainTextBytes.Length - 1
plainTextWithSaltBytes(I) = plainTextBytes(I)
Next I
' Append salt bytes to the resulting array.
For I = 0 To saltBytes.Length - 1
plainTextWithSaltBytes(plainTextBytes.Length + I) = saltBytes(I)
Next I
' Because we support multiple hashing algorithms, we must define
' hash object as a common (abstract) base class. We will specify the
' actual hashing algorithm class later during object creation.
Dim hash As HashAlgorithm = New SHA1Managed()
' Compute hash value of our plain text with appended salt.
Dim hashBytes As Byte() = hash.ComputeHash(plainTextWithSaltBytes)
' Create array which will hold hash and original salt bytes.
Dim hashWithSaltBytes() As Byte = New Byte(hashBytes.Length + saltBytes.Length - 1) {}
' Copy hash bytes into resulting array.
For I = 0 To hashBytes.Length - 1
hashWithSaltBytes(I) = hashBytes(I)
Next I
' Append salt bytes to the result.
For I = 0 To saltBytes.Length - 1
hashWithSaltBytes(hashBytes.Length + I) = saltBytes(I)
Next I
' Convert result into a base64-encoded string.
Dim hashValue As String
hashValue = Base64Converter.ConvertToBase64(hashWithSaltBytes)
' Return the result.
Return hashValue
End Function
''' <summary>
''' Compares a hash of the specified plain text value to a given hash value. Plain text is hashed with the same salt value as the original hash.
''' </summary>
''' <param name="plainText">Plain text to be verified against the specified hash. The function does not check whether this parameter is null.</param>
''' <param name="hashValue">Base64-encoded hash value produced by ComputeHash function. This value includes the original salt appended to it.</param>
''' <returns>Computed hash mathes the specified hash is true, otherwise false.</returns>
''' <remarks></remarks>
Public Shared Function VerifySHA1Hash(ByVal plainText As String, ByVal hashValue As String) As Boolean
' Convert base64-encoded hash value into a byte array.
Dim hashWithSaltBytes As Byte() = Base64Converter.ConvertBytesFromBase64(hashValue)
' We must know size of hash (without salt).
Dim hashSizeInBits As Integer
Dim hashSizeInBytes As Integer
hashSizeInBits = 160
' Convert size of hash from bits to bytes.
hashSizeInBytes = CInt(hashSizeInBits / 8)
' Make sure that the specified hash value is long enough.
If (hashWithSaltBytes.Length < hashSizeInBytes) Then
VerifySHA1Hash = False
End If
' Allocate array to hold original salt bytes retrieved from hash.
Dim saltBytes() As Byte = New Byte(hashWithSaltBytes.Length - hashSizeInBytes - 1) {}
' Copy salt from the end of the hash to the new array.
For I As Integer = 0 To saltBytes.Length - 1
saltBytes(I) = hashWithSaltBytes(hashSizeInBytes + I)
Next I
' Compute a new hash string.
Dim expectedHashString As String = ComputeSHA1Hash(plainText, saltBytes)
' If the computed hash matches the specified hash,
' the plain text value must be correct.
Return (hashValue = expectedHashString)
End Function
#End Region
#Region "AES256 Encrypt/Decypt"
''' <summary>
''' Encrypts specified plaintext using Rijndael(AES) symmetric key algorithm and returns a base64-encoded result.
''' </summary>
''' <returns>Encrypted value formatted as a base64-encoded string.</returns>
''' <remarks></remarks>
Public Shared Function AES256Encrypt(ByVal plainText As String, Optional ByVal key As String = Nothing, Optional ByVal IV As String = Nothing, Optional ByVal salt As String = Nothing) As String
' Convert our plaintext into a byte array.
Dim plainTextBytes As Byte() = Encoding.ASCII.GetBytes(plainText)
Return AES256Encrypt(plainTextBytes, key, IV, salt)
End Function
Public Shared Function AES256Encrypt(ByVal plainTextBytes As Byte(), Optional ByVal key As String = Nothing, Optional ByVal IV As String = Nothing, Optional ByVal salt As String = Nothing) As String
Dim CryptKey As System.Security.Cryptography.PasswordDeriveBytes
If salt Is Nothing Then
CryptKey = GetCryptKey(key)
Else
CryptKey = GetCryptKey(key, Encoding.ASCII.GetBytes(salt))
End If
' Use the password to generate pseudo-random bytes for the encryption
' key. Specify the size of the key in bytes (instead of bits).
Dim keyBytes As Byte() = CryptKey.GetBytes(CInt(256 / 8))
' Create uninitialized Rijndael encryption object.
Dim symmetricKey As New RijndaelManaged()
' It is reasonable to set encryption mode to Cipher Block Chaining
' (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.CBC
' Generate encryptor from the existing key bytes and initialization
' vector. Key size will be defined based on the number of the key
' bytes.
If String.IsNullOrEmpty(IV) Then
IV = COMP_KEY
Else
If IV.Length < 16 Then
IV = IV & COMP_KEY.Substring(0, 16 - IV.Length)
ElseIf IV.Length > 16 Then
IV = IV.Substring(0, 16)
End If
End If
Dim encryptor As ICryptoTransform = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(IV))
' Define memory stream which will be used to hold encrypted data.
Dim memoryStream As New MemoryStream()
' Define cryptographic stream (always use Write mode for encryption).
Dim cryptoStream As New CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)
' Start encrypting.
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length)
' Finish encrypting.
cryptoStream.FlushFinalBlock()
' Convert our encrypted data from a memory stream into a byte array.
Dim cipherTextBytes As Byte() = memoryStream.ToArray()
' Close both streams.
memoryStream.Close()
cryptoStream.Close()
' Convert encrypted data into a base64-encoded string.
Dim cipherText As String
cipherText = Base64Converter.ConvertToBase64(cipherTextBytes)
' Return encrypted string.
Return cipherText
End Function
''' <summary>
''' Decrypts specified ciphertext using Rijndael(AES) symmetric key algorithm.
''' </summary>
''' <returns>Decrypted string value.</returns>
''' <remarks></remarks>
Public Shared Function AES256Decrypt(ByVal cipherText As String, Optional ByVal key As String = Nothing, Optional ByVal IV As String = Nothing, Optional ByVal salt As String = Nothing) As String
' Convert our ciphertext into a byte array.
Dim cipherTextBytes As Byte() = Base64Converter.ConvertBytesFromBase64(cipherText)
Dim CryptKey As System.Security.Cryptography.PasswordDeriveBytes
If salt Is Nothing Then
CryptKey = GetCryptKey(key)
Else
CryptKey = GetCryptKey(Nothing, Encoding.ASCII.GetBytes(salt))
End If
' Use the password to generate pseudo-random bytes for the encryption
' key. Specify the size of the key in bytes (instead of bits).
Dim keyBytes As Byte() = CryptKey.GetBytes(CInt(256 / 8))
' Create uninitialized Rijndael encryption object.
Dim symmetricKey As New RijndaelManaged()
' It is reasonable to set encryption mode to Cipher Block Chaining
' (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.CBC
' Generate decryptor from the existing key bytes and initialization
' vector. Key size will be defined based on the number of the key
' bytes.
If String.IsNullOrEmpty(IV) Then
IV = COMP_KEY
Else
If IV.Length < 16 Then
IV = IV & COMP_KEY.Substring(0, 16 - IV.Length)
ElseIf IV.Length > 16 Then
IV = IV.Substring(0, 16)
End If
End If
Dim decryptor As ICryptoTransform = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(COMP_KEY))
' Define memory stream which will be used to hold encrypted data.
Dim memoryStream As New MemoryStream(cipherTextBytes)
' Define memory stream which will be used to hold encrypted data.
Dim cryptoStream As New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)
' Since at this point we don't know what the size of decrypted data
' will be, allocate the buffer long enough to hold ciphertext;
' plaintext is never longer than ciphertext.
Dim plainTextBytes As Byte()
ReDim plainTextBytes(cipherTextBytes.Length)
' Start decrypting.
Dim decryptedByteCount As Integer = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
' Close both streams.
memoryStream.Close()
cryptoStream.Close()
' Convert decrypted data into a string.
' Let us assume that the original plaintext string was UTF8-encoded.
Dim plainText As String = Encoding.ASCII.GetString(plainTextBytes, 0, decryptedByteCount)
' Return decrypted string.
Return plainText
End Function
Private Shared Function GetCryptKey(Optional ByVal key As String = Nothing, Optional ByVal salt As Byte() = Nothing) As System.Security.Cryptography.PasswordDeriveBytes
If String.IsNullOrEmpty(key) Then
key = COMP_KEY
Else
If key.Length < 16 Then key = key & COMP_KEY.Substring(0, 16 - key.Length)
End If
If salt Is Nothing Then salt = System.Text.Encoding.ASCII.GetBytes(COMP_IV)
Return New System.Security.Cryptography.PasswordDeriveBytes(key, salt, COMP_KEY_HASH, COMP_KEY_ITERATION)
End Function
#End Region
#Region "TripleDES Decyption"
Public Shared Function TripleDESDecrypt(ByVal cipertext As String) As String
Try
Dim keyFileLocation As String = ConfigReaderHelper.GetSetting("KeyFile")
Dim cm As SSCrypto.CryptoModule = New SSCrypto.CryptoModule()
Dim keyByte As Byte() = File.ReadAllBytes(keyFileLocation)
Dim key As String = cm.UnMassageKey(keyByte)
Dim data As Byte() = Base64Converter.ConvertBytesFromBase64(cipertext)
Dim result As Byte() = cm.DecryptTripleDES(key, data)
Return Encoding.UTF8.GetString(result)
Catch ex As Exception
'LogFile.WriteError(">Viewer Unable to Decrypt data. Except = " + ex.ToString())
Return String.Empty
End Try
End Function
#End Region
End Class
End Namespace