Implement Encryption

Feb 13, 2011 at 7:56 AM

Hi,

 

I was wondering if you could implement encryption?

How would this be done? Should we store the encryption key in the indexpage?
Any suggestions on how to do this?

Great tool! Keep up the good work!

 

Grtz Kraeven

Knowledge needs to be shared to enlighten the world! ~ Kraeven 

Feb 13, 2011 at 4:01 PM

Hi kraeven,

Yes, it's will be on the next version of FileDB. The ideia for encryption is be use only one password for a hole file storage (not one for each file inside). I will use the header page to store not the key, but a fixed word encrypted with user  key. Then, when user open FileDB with password, I will check decryption this fixed word. After that, only data file will be encrypted before write on disk and decrypt after read from disk. I'm still studing libraries for do this in a simple way.

Regards,

 

    Maurício David

Feb 13, 2011 at 6:50 PM

 

Perfect!
Looking forward to the next release ;-)
BTW I don't know if it's worth something to you, but this is the class I use (VB.NET) to encrypt/decrypt AES/Rijndael
Imports System.Text
Imports System.Text.UnicodeEncoding
Imports System.Security.Cryptography
Imports System.IO

Friend NotInheritable Class EncryptEngine

#Region "Private Variables "


    Private bytKey() As Byte
    Private bytIV() As Byte
    Private bInitialised As Boolean = False
    Private rijM As RijndaelManaged = Nothing
    Private bCancel As Boolean = False


    Dim strFileToEncrypt As String
    Dim strFileToDecrypt As String
    Dim strOutputEncrypt As String
    Dim strOutputDecrypt As String


#End Region

#Region " Public Events_Enums "

    Public Event Progress(ByVal prog As Integer)
    Public Event Finished(ByVal retType As ReturnCode)

    Public Enum ReturnCode As Integer
        Good = 0
        Bad = 1
        IncorrectPassword = 2
    End Enum

    Public Enum CryptoAction
        'Define the enumeration for CryptoAction.
        ActionEncrypt = 1
        ActionDecrypt = 2
    End Enum

#End Region

#Region "Private Functions"

    Private Function CreateIV(ByVal strPassword As String) As Byte()
        'Convert strPassword to an array and store in chrData.
        Dim chrData() As Char = strPassword.ToCharArray
        'Use intLength to get strPassword size.
        Dim intLength As Integer = chrData.GetUpperBound(0)
        'Declare bytDataToHash and make it the same size as chrData.
        Dim bytDataToHash(intLength) As Byte

        'Use For Next to convert and store chrData into bytDataToHash.
        For i As Integer = 0 To chrData.GetUpperBound(0)
            bytDataToHash(i) = CByte(Asc(chrData(i)))
        Next

        'Declare what hash to use.
        Dim SHA512 As New System.Security.Cryptography.SHA512Managed
        'Declare bytResult, Hash bytDataToHash and store it in bytResult.
        Dim bytResult As Byte() = SHA512.ComputeHash(bytDataToHash)
        'Declare bytIV(15).  It will hold 128 bits.
        Dim bytIV(15) As Byte

        'Use For Next to put a specific size (128 bits) of 
        'bytResult into bytIV. The 0 To 30 for bytKey used the first 256 bits.
        'of the hashed password. The 32 To 47 will put the next 128 bits into bytIV.
        For i As Integer = 32 To 47
            bytIV(i - 32) = bytResult(i)
        Next

        Return bytIV 'return the IV
    End Function

    Private Function CreateKey(ByVal strPassword As String) As Byte()
        'Convert strPassword to an array and store in chrData.
        Dim chrData() As Char = strPassword.ToCharArray
        'Use intLength to get strPassword size.
        Dim intLength As Integer = chrData.GetUpperBound(0)
        'Declare bytDataToHash and make it the same size as chrData.
        Dim bytDataToHash(intLength) As Byte

        'Use For Next to convert and store chrData into bytDataToHash.
        For i As Integer = 0 To chrData.GetUpperBound(0)
            bytDataToHash(i) = CByte(Asc(chrData(i)))
        Next

        'Declare what hash to use.
        Dim SHA512 As New System.Security.Cryptography.SHA512Managed
        'Declare bytResult, Hash bytDataToHash and store it in bytResult.
        Dim bytResult As Byte() = SHA512.ComputeHash(bytDataToHash)
        'Declare bytKey(31).  It will hold 256 bits.
        Dim bytKey(31) As Byte

        'Use For Next to put a specific size (256 bits) of 
        'bytResult into bytKey. The 0 To 31 will put the first 256 bits
        'of 512 bits into bytKey.
        For i As Integer = 0 To 31
            bytKey(i) = bytResult(i)
        Next

        Return bytKey 'Return the key.
    End Function

#End Region

#Region "Public Functions/Methods"

    Public Sub Initialise(ByVal sPWH As String)
        'initialise rijM
        rijM = New RijndaelManaged

        bytKey = CreateKey(sPWH)
        bytIV = CreateIV(sPWH)

        bInitialised = True
    End Sub

    Public Sub CancelTransform()
        If Not bInitialised Then Return
        bCancel = True
    End Sub

    Public Function GenerateHash(ByVal strSource As String) As String
        Return System.Convert.ToBase64String(New SHA384Managed().ComputeHash(New UnicodeEncoding().GetBytes(strSource)))
    End Function

    Public Sub EncryptOrDecryptFile(ByVal strInputFile As String, _
                                    ByVal strOutputFile As String, _
                                    ByVal Direction As CryptoAction, _
                                    Optional ByRef ErrMessage As String = "")
        Dim retVal As ReturnCode = ReturnCode.Bad
        Dim fsInput As System.IO.FileStream = Nothing
        Dim fsOutput As System.IO.FileStream = Nothing
        If Not bInitialised Then RaiseEvent Finished(ReturnCode.Bad)
        If Not IO.File.Exists(strInputFile) Then RaiseEvent Finished(ReturnCode.Bad)

        Try 'In case of errors.

            'Setup file streams to handle input and output.
            fsInput = New System.IO.FileStream(strInputFile, FileMode.Open, _
                                               FileAccess.Read)
            fsOutput = New System.IO.FileStream(strOutputFile, FileMode.OpenOrCreate, _
                                                FileAccess.Write)
            fsOutput.SetLength(0) 'make sure fsOutput is empty

            'Declare variables for encrypt/decrypt process.
            Dim bytBuffer(4096) As Byte 'holds a block of bytes for processing
            Dim lngBytesProcessed As Long = 0 'running count of bytes processed
            Dim lngFileLength As Long = fsInput.Length 'the input file's length
            Dim intBytesInCurrentBlock As Integer 'current bytes being processed
            Dim csCryptoStream As CryptoStream = Nothing
            'Declare your CryptoServiceProvider.
            Dim cspRijndael As New System.Security.Cryptography.RijndaelManaged


            'Determine if ecryption or decryption and setup CryptoStream.
            Select Case Direction
                Case CryptoAction.ActionEncrypt
                    csCryptoStream = New CryptoStream(fsOutput, _
                    cspRijndael.CreateEncryptor(bytKey, bytIV), _
                    CryptoStreamMode.Write)

                Case CryptoAction.ActionDecrypt
                    csCryptoStream = New CryptoStream(fsOutput, _
                    cspRijndael.CreateDecryptor(bytKey, bytIV), _
                    CryptoStreamMode.Write)
            End Select

            'Use While to loop until all of the file is processed.
            While lngBytesProcessed < lngFileLength
                If bCancel Then Exit Try
                'Read file with the input filestream.
                intBytesInCurrentBlock = fsInput.Read(bytBuffer, 0, 4096)
                'Write output file with the cryptostream.
                csCryptoStream.Write(bytBuffer, 0, intBytesInCurrentBlock)
                'Update lngBytesProcessed
                lngBytesProcessed = lngBytesProcessed + CLng(intBytesInCurrentBlock)
                'Update Progress
                RaiseEvent Progress(CInt((lngBytesProcessed / lngFileLength) * 100))
            End While
            csCryptoStream.FlushFinalBlock()
            'Close FileStreams and CryptoStream.
            csCryptoStream.Close()
            fsInput.Close()
            fsOutput.Close()

            'If encrypting then delete the original unencrypted file.
            If Direction = CryptoAction.ActionEncrypt Then
                Dim fileOriginal As New FileInfo(strFileToEncrypt)
                fileOriginal.Delete()
            End If

            'If decrypting then delete the encrypted file.
            If Direction = CryptoAction.ActionDecrypt Then
                Dim fileEncrypted As New FileInfo(strFileToDecrypt)
                fileEncrypted.Delete()
            End If
            retVal = ReturnCode.Good
            RaiseEvent Progress(100)
            RaiseEvent Finished(retVal)
            'Catch file not found error.
        Catch When Err.Number = 53 'if file not found
            ErrMessage = "Please check to make sure the path and filename are correct and if the file exists."
            'Catch all other errors. And delete partial files.
            retVal = ReturnCode.Bad
            RaiseEvent Progress(100)
            RaiseEvent Finished(retVal)
        Catch ex As Exception
            fsInput.Close()
            fsOutput.Close()
            Dim fileDelete As New FileInfo(strOutputFile)
            fileDelete.Delete()
            retVal = ReturnCode.Bad
            ErrMessage = ex.ToString
            RaiseEvent Progress(100)
            RaiseEvent Finished(retVal)
        End Try
        RaiseEvent Progress(100)
        RaiseEvent Finished(retVal)
    End Sub

    Public Sub EncryptOrDecryptStream(ByVal InStream As MemoryStream, _
                                 ByRef OutStream As MemoryStream, _
                                 ByVal Direction As CryptoAction, _
                                 Optional ByRef ErrMessage As String = "")
        Dim retVal As ReturnCode = ReturnCode.Bad

        If Not bInitialised Then RaiseEvent Finished(ReturnCode.Bad)
        If InStream Is Nothing Then RaiseEvent Finished(ReturnCode.Bad)
        Dim csCryptoStream As CryptoStream = Nothing
        Try 'In case of errors.
            OutStream = New MemoryStream
            InStream.Seek(0, SeekOrigin.Begin)


            'Declare variables for encrypt/decrypt process.
            Dim bytBuffer(4096) As Byte 'holds a block of bytes for processing
            Dim lngBytesProcessed As Long = 0 'running count of bytes processed
            Dim lngFileLength As Long = InStream.Length 'the input stream length
            Dim intBytesInCurrentBlock As Integer 'current bytes being processed
            'Declare your CryptoServiceProvider.
            Dim cspRijndael As New System.Security.Cryptography.RijndaelManaged


            'Determine if ecryption or decryption and setup CryptoStream.
            Select Case Direction
                Case CryptoAction.ActionEncrypt
                    csCryptoStream = New CryptoStream(OutStream, _
                    cspRijndael.CreateEncryptor(bytKey, bytIV), _
                    CryptoStreamMode.Write)

                Case CryptoAction.ActionDecrypt
                    csCryptoStream = New CryptoStream(OutStream, _
                    cspRijndael.CreateDecryptor(bytKey, bytIV), _
                    CryptoStreamMode.Write)
            End Select
            Try
                'Use While to loop until all of the file is processed.
            While lngBytesProcessed < lngFileLength
                'Read file with the input filestream.
                If bCancel Then Exit Try
                intBytesInCurrentBlock = InStream.Read(bytBuffer, 0, 4096)
                'Write output file with the cryptostream.
                csCryptoStream.Write(bytBuffer, 0, intBytesInCurrentBlock)
                'Update lngBytesProcessed
                lngBytesProcessed = lngBytesProcessed + CLng(intBytesInCurrentBlock)
                'Update Progress Bar
                RaiseEvent Progress(CInt((lngBytesProcessed / lngFileLength) * 100))
            End While

            csCryptoStream.FlushFinalBlock()

            If Not InStream Is Nothing Then
                InStream.Close()
            End If
            retVal = ReturnCode.Good

                'Catch worong password
            Catch ex As CryptographicException
                If Not InStream Is Nothing Then
                    InStream.Close()
                End If
                If Not csCryptoStream Is Nothing Then csCryptoStream = Nothing
                If Not OutStream Is Nothing Then
                    OutStream.Close()
                End If
                ErrMessage = "Unable to " + IIf(Direction = CryptoAction.ActionDecrypt, "decrypt", "encrypt") + " file." + Environment.NewLine + "Please check your password!" + Environment.NewLine + ex.Message
                retVal = ReturnCode.IncorrectPassword
                '  Throw New CryptographicException("Unable to " + IIf(Direction = CryptoAction.ActionDecrypt, "decrypt", "encrypt") + " file." + Environment.NewLine + "Please check your password!", ex)
                RaiseEvent Progress(100)
                RaiseEvent Finished(retVal)
        End Try


        Catch ex As Exception

            If Not InStream Is Nothing Then
                InStream.Close()
            End If
            If Not csCryptoStream Is Nothing Then csCryptoStream = Nothing
            If Not OutStream Is Nothing Then
                OutStream.Close()
            End If
            ErrMessage = "Error occured while trying to encrypt/decrypt file." + Environment.NewLine + ex.ToString
            retVal = ReturnCode.Bad
            RaiseEvent Progress(100)
            RaiseEvent Finished(retVal)
        End Try
        RaiseEvent Progress(100)
        RaiseEvent Finished(retVal)
    End Sub
#End Region

End Class

Public Class Encryptor

    Private WithEvents CryptorEngine As New EncryptEngine
    Private isEncrypt As Boolean = True
    Private isFileEncrypt As Boolean
    Private file As String = ""
    Private MemStream As MemoryStream = Nothing
    Private tempDir As String = ""
    Private tempFile As String = ""

    Private _Progress As Integer
    Private _IsFinished As Boolean = False
    Private _ReturnedCode As ReturnCode
    Private _CryptorFile As IO.FileInfo = Nothing
    Private _CryptorStream As MemoryStream = Nothing
    Private _ErrorMessage As String = ""

#Region "Constructor"

    Public Sub New(ByVal filePath As String, ByVal pass As String, Optional ByVal returnErr As String = "")
        Me.file = filePath
        Me.isFileEncrypt = True
        If pass < 8 Then
            returnErr = "Invalid password : less than 8 characters long." & vbCrLf & "This is not long enough to properly secure the file."
        Else
            CryptorEngine.Initialise(CryptorEngine.GenerateHash(pass))
            tempDir = Environment.GetEnvironmentVariable("temp") & "\KaoticCryptor"
            tempFile = tempDir & "\~kaotic_enc"
            If Not IO.Directory.Exists(tempDir) Then IO.Directory.CreateDirectory(tempDir)
        End If
    End Sub

    Public Sub New(ByVal _MemStream As MemoryStream, ByVal pass As String, Optional ByVal returnErr As String = "")
        Me.MemStream = _MemStream
        Me.isFileEncrypt = False
        If pass.Length < 8 Then
            returnErr = "Invalid password : less than 8 characters long." & vbCrLf & "This is not long enough to properly secure the file."
        Else
            CryptorEngine.Initialise(CryptorEngine.GenerateHash(pass))
        End If
    End Sub

    Public Sub New(ByVal _MemStream As FileStream, ByVal pass As String, Optional ByVal returnErr As String = "")
        Me.MemStream = FileStream2MemoryStream(_MemStream)
        Me.isFileEncrypt = False
        If pass.Length < 8 Then
            returnErr = "Invalid password : less than 8 characters long." & vbCrLf & "This is not long enough to properly secure the file."
        Else
            CryptorEngine.Initialise(CryptorEngine.GenerateHash(pass))
        End If
    End Sub


#End Region

#Region "Public Methods"


    Public Sub Encrypt()
        RunTransform(True)
    End Sub

    Public Sub Decrypt()
        RunTransform(False)
    End Sub

    Public Sub CancelEncryption()
        CryptorEngine.CancelTransform()
    End Sub

#End Region

#Region "Properties"
    Public Property Progress() As Integer
        Get
            Return _Progress
        End Get
        Set(ByVal value As Integer)
            _Progress = value
        End Set
    End Property

    Public Enum ReturnCode As Integer
        Good = 0
        Bad = 1
        IncorrectPassword = 2
    End Enum

    Public ReadOnly Property CryptorStream() As MemoryStream
        Get
            Return _CryptorStream
        End Get
    End Property

    Public ReadOnly Property CryptorFile() As IO.FileInfo
        Get
            Return _CryptorFile
        End Get
    End Property

    Public Property ErrorMessage() As String
        Get
            Return _ErrorMessage
        End Get
        Set(ByVal value As String)
            _ErrorMessage = value
        End Set
    End Property

    Public Property ReturnedCode() As ReturnCode
        Get
            Return _ReturnedCode
        End Get
        Set(ByVal value As ReturnCode)
            _ReturnedCode = value
        End Set
    End Property

#End Region

#Region "Private Methods"

    Private Sub RunTransform(ByVal isEncrypt As Boolean)
        Me.ErrorMessage = ""
        If Me.isFileEncrypt Then
            CryptorEngine.EncryptOrDecryptFile(file, tempFile, IIf(isEncrypt, EncryptEngine.CryptoAction.ActionEncrypt, EncryptEngine.CryptoAction.ActionDecrypt), Me.ErrorMessage)
            
            Me._CryptorFile = New IO.FileInfo(file)
        Else
            Dim ReturnSteam As New MemoryStream
            CryptorEngine.EncryptOrDecryptStream(Me.MemStream, ReturnSteam, IIf(isEncrypt, EncryptEngine.CryptoAction.ActionEncrypt, EncryptEngine.CryptoAction.ActionDecrypt), Me.ErrorMessage)
            
            Me._CryptorStream = ReturnSteam
            If Not Me.MemStream Is Nothing Then
                Me.MemStream.Close()
            End If
        End If
    End Sub

    Private Function FileStream2MemoryStream(ByVal FStream As FileStream) As MemoryStream

        Dim MStream As New MemoryStream(FStream.Length)

        Dim BinReader As BinaryReader = New BinaryReader(FStream)

        BinReader.BaseStream.Position = 0

        Dim byteBuffer() As Byte = BinReader.ReadBytes(FStream.Length)

        MStream.Write(byteBuffer, 0, FStream.Length)
        MStream.Flush()
        MStream.Position = 0

        Return MStream

    End Function

#End Region

#Region "Events"

    Private Sub Cryptor_Progress(ByVal prog As Integer) Handles CryptorEngine.Progress
        Me.Progress = CInt(IIf(prog > 100, 100, prog))
    End Sub

    Private Sub Cryptor_Finished(ByVal retType As ReturnCode) Handles CryptorEngine.Finished

        Select Case retType
            Case ReturnCode.Good
                If isFileEncrypt Then
                    IO.File.Delete(file)
                    IO.File.Move(tempFile, file)
                End If
                Me.ReturnedCode = ReturnCode.Good
            Case ReturnCode.Bad
                If isFileEncrypt Then
                    IO.File.Delete(tempFile)
                End If
                Me.ReturnedCode = ReturnCode.Bad
            Case ReturnCode.IncorrectPassword
                Me.ReturnedCode = ReturnCode.IncorrectPassword
        End Select
        If isFileEncrypt Then
            If IO.Directory.Exists(tempDir) Then IO.Directory.Delete(tempDir)
        End If
    End Sub

#End Region

End Class

 

 Using like this this :

   Private Function EncryptVFile(ByVal memstream As IO.MemoryStream, ByVal password As String) As IO.MemoryStream
            Try

                'use encryptor to encrypt mem stream
                Dim errSTR As String = ""
                Dim EnCryptEngine As Encryptor

          
                EnCryptEngine = New Encryptor(CType(memstream, MemoryStream), password, errSTR)

                If errSTR <> "" Then
                    Throw New Exception("Wrong password given!")
                End If

                With EnCryptEngine
                    .Encrypt()
                End With

                Select Case EnCryptEngine.ReturnedCode
                    Case Encryptor.ReturnCode.Good
                        memstream = EnCryptEngine.CryptorStream
                        memstream.Seek(0, SeekOrigin.Begin)
                    Case Encryptor.ReturnCode.Bad
                        MsgBox("Unable to encrypt Virtual File!" + vbCrLf + EnCryptEngine.ErrorMessage)
                        Return Nothing
                    Case Encryptor.ReturnCode.IncorrectPassword
                        MsgBox("Wrong password given!" + vbCrLf + EnCryptEngine.ErrorMessage)
                        Return Nothing
                End Select
                Return memstream
            Catch ex As Exception
                MsgBox("Unable to Encrypt Virtual File!" + vbCrLf + ex.Message)
                Return Nothing
            End Try
        End Function


 Private Function DecryptVFile(ByVal memStream As IO.MemoryStream, ByVal password As String) As IO.MemoryStream
            Try

                'use encryptor to decrypt mem stream
                Dim errSTR As String = ""
                Dim EnCryptEngine As New Encryptor(memStream, password, errSTR)

                If errSTR <> "" Then
                    Throw New Exception("Wrong password given!")
                End If

                With EnCryptEngine
                    .Decrypt()
                End With

                Select Case EnCryptEngine.ReturnedCode
                    Case Encryptor.ReturnCode.Good
                        memStream = EnCryptEngine.CryptorStream
                        'memStream.Position = 0
                        memStream.Seek(0, IO.SeekOrigin.Begin)
                        Return memStream
                    Case Encryptor.ReturnCode.Bad
                        MsgBox("Unable to decrypt VFile!" + vbCrLf + EnCryptEngine.ErrorMessage)
                        Return Nothing
                    Case Encryptor.ReturnCode.IncorrectPassword
                        MsgBox("Wrong password given!" + vbCrLf + EnCryptEngine.ErrorMessage)
                        Return Nothing
                End Select


                Return memStream

            Catch ex As Exception
                MsgBox("Unable to decrypt VFile!" + vbCrLf + ex.Message)
                Return Nothing
            End Try
    
        End Function