Imports System.IO Imports System.Runtime.Serialization.Formatters.Binary Imports DigitalData.Modules.Logging ''' FileContainer ''' 0.0.0.1 ''' 16.11.2018 ''' ''' File Container for securely saving files ''' ''' ''' NLog, >= 4.5.8 ''' ''' ''' LogConfig, DigitalData.Module.Logging.LogConfig ''' A LogConfig object ''' ''' ''' ''' ''' ''' ''' Public Class FileContainer Private _files As List(Of FileEntry) Private _crypto As Encryption Private _compression As Compression Private _formatter As BinaryFormatter Private _containerId As Guid Private _inner As FileContainerInner Private _logger As Logger Private _logConfig As LogConfig Public Class FileEntry Public FileId As String Public Contents As Byte() Public CreatedAt As DateTime Public UpdatedAt As DateTime End Class Public Class FileContainerInner Public Files As List(Of FileEntry) Public ContainerID As Guid Public CreatedAt As DateTime Public UpdatedAt As DateTime End Class Public ReadOnly Property ContainerId As String Get If _inner Is Nothing Then Return Nothing End If Return _inner.ContainerID.ToString End Get End Property ''' ''' Gibt eine Auflistung der Dateien in Container zurück. ''' ''' Eine Liste von Objekten der Klasse FileContainer.FileEntry Public ReadOnly Property Files As List(Of FileEntry) Get If _inner Is Nothing Then Return Nothing End If Return _inner.Files End Get End Property ''' ''' Erstellt eine Representation eines Datei Containers, der mit dem angegebenen Passwort geschützt ist. ''' Ist für das speichern von neuen Containern und für das laden vorhandener Container nötig. ''' ''' Das Passwort, mit dem der Container ver- und entschlüsselt wird ''' ''' Dim password = "meinpasswort" ''' Dim container = new FileContainer(password) ''' Public Sub New(LogConfig As LogConfig, Password As String) _logger = LogConfig.GetLogger() _crypto = New Encryption(LogConfig, Password) _compression = New Compression(LogConfig) _formatter = New BinaryFormatter _inner = New FileContainerInner() With { .Files = New List(Of FileEntry), .ContainerID = Guid.NewGuid(), .CreatedAt = New DateTime() } End Sub ''' ''' Speichert einen Datei Container am angegebenen Pfad. ''' Davor werden die Dateien in Files komprimiert und dann verschlüsselt. ''' ''' Der Pfad mit Dateiname, unter dem der Container abgelegt wird. ''' ''' ''' container.AddFile("datei1.txt") ''' container.AddFile("datei2.pdf") ''' container.Save("container.enc") ''' Public Sub Save(Path As String) Try ' 1. Serialize Dim oFileContainerInner As Byte() = Serialize(_inner) ' 2. Compress Dim oFileContainerCompressed As Byte() = _compression.Compress(oFileContainerInner) ' 3. Encrypt Dim oFileContainerEncrypted As Byte() = _crypto.Encrypt(oFileContainerCompressed) BytesToFile(oFileContainerEncrypted, Path) Catch ex As Exception _logger.Error(ex) Throw ex End Try End Sub ''' ''' Speichert einen Datei Container am angegebenen Pfad. ''' Davor werden die Dateien in Files komprimiert und dann verschlüsselt. ''' ''' Der Pfad mit Dateiname, unter dem der Container abgelegt wird. ''' ''' Public Async Sub foobar() ''' ... ''' container.AddFile("datei1.txt") ''' container.AddFile("datei2.pdf") ''' await container.SaveAsync("container.enc") ''' ... ''' End Sub ''' Public Async Function SaveAsync(Path As String) As Task Try ' 1. Serialize Dim oFileContainerInner As Byte() = Serialize(_inner) ' 2. Compress Dim oFileContainerCompressed As Byte() = Await _compression.CompressAsync(oFileContainerInner) 'Dim bFilesCompressed = bFiles ' 3. Encrypt Dim oFileContainerEncrypted As Byte() = Await _crypto.EncryptAsync(oFileContainerCompressed) BytesToFileAsync(oFileContainerEncrypted, Path) Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function ''' ''' Lädt einen Datei Container aus dem angegebenen Pfad. ''' Anschließend werden die enthaltenen Dateien entschlüsselt, dekomprimiert und in Files abgelegt. ''' ''' Der Pfad mit Dateiname, von dem der Container geladen wird. ''' ''' ''' container.Load("container.enc") ''' Dim files As List(Of FileContainer.FileEntry) = container.Files ''' Public Sub Load(Path As String) Try Dim oContainerInnerEncrypted As Byte() = FileToBytes(Path) ' 1. Decrypt Dim oFileContainerCompressed As Byte() = _crypto.Decrypt(oContainerInnerEncrypted) ' 2. Decompress Dim oFileContainerInner As Byte() = _compression.Decompress(oFileContainerCompressed) ' 3. Deserialize _inner = Deserialize(oFileContainerInner) Catch ex As Exception _logger.Error(ex) Throw ex End Try End Sub ''' ''' Lädt einen Datei Container aus dem angegebenen Pfad. ''' Anschließend werden die enthaltenen Dateien entschlüsselt, dekomprimiert und in Files abgelegt. ''' ''' Der Pfad mit Dateiname, von dem der Container geladen wird. ''' ''' Public Async Sub foobar() ''' ... ''' await container.LoadAsync("container.enc") ''' Dim files As List(Of FileContainer.FileEntry) = container.Files ''' ... ''' End Sub ''' Public Async Function LoadAsync(Path As String) As Task Try Dim oContainerInnerEncrypted As Byte() = Await FileToBytesAsync(Path) ' 1. Decrypt Dim oFileContainerCompressed As Byte() = Await _crypto.DecryptAsync(oContainerInnerEncrypted) ' 2. Decompress Dim oFileContainerInner As Byte() = Await _compression.DecompressAsync(oFileContainerCompressed) ' 3. Deserialize _inner = Deserialize(oFileContainerInner) Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function ''' ''' Fügt einem Datei Container eine neue Datei hinzu ''' ''' Der Pfad zur Datei, die im Container gespeichert werden soll ''' Public Sub AddFile(FilePath As String) Try Dim oFileInfo As New FileInfo(FilePath) If oFileInfo.Exists = False Then Throw New FileNotFoundException($"{FilePath} is not a valid path.") End If AddFile(IO.File.ReadAllBytes(FilePath)) Catch ex As Exception _logger.Error(ex) Throw ex End Try End Sub Public Sub AddFile(FileContents As Byte()) Try Dim oFileEntry As New FileEntry With { .Contents = FileContents, .FileId = Guid.NewGuid().ToString() } _inner.Files.Add(oFileEntry) Catch ex As Exception _logger.Error(ex) Throw ex End Try End Sub Public Function GetFile(FileId As String) As FileEntry Return _inner.Files.Where(Function(f) f.FileId = FileId).FirstOrDefault() End Function Private Function Serialize(InnerData As FileContainerInner) As Byte() Using oStream As New MemoryStream _formatter.Serialize(oStream, InnerData) Return oStream.ToArray() End Using End Function Private Function Deserialize(InnerData As Byte()) As FileContainerInner Using oStream As New MemoryStream(InnerData) Dim oInner = TryCast(_formatter.Deserialize(oStream), FileContainerInner) Return oInner End Using End Function Private Sub BytesToFile(Data As Byte(), FilePath As String) IO.File.WriteAllBytes(FilePath, Data) End Sub Private Function FileToBytes(FilePath As String) As Byte() Return IO.File.ReadAllBytes(FilePath) End Function Private Async Function FileToBytesAsync(FilePath As String) As Task(Of Byte()) Using oFileStream = New FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, True) Dim oBuffer As Byte() = New Byte(oFileStream.Length - 1) {} Await oFileStream.ReadAsync(oBuffer, 0, oFileStream.Length) oFileStream.Close() Return oBuffer End Using End Function Private Async Sub BytesToFileAsync(Data As Byte(), FilePath As String) Using oSourceStream As New FileStream(FilePath, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize:=4096, useAsync:=True) Await oSourceStream.WriteAsync(Data, 0, Data.Length) End Using End Sub End Class