Skip to content
2 changes: 1 addition & 1 deletion src/Orc.SupportPackage.Example/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Orc.SupportPackage.Example;
namespace Orc.SupportPackage.Example;

using System.Globalization;
using System.Windows;
Expand Down
90 changes: 88 additions & 2 deletions src/Orc.SupportPackage.Example/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Orc.SupportPackage.Example.ViewModels;
namespace Orc.SupportPackage.Example.ViewModels;

using System;
using System.Drawing.Imaging;
Expand All @@ -24,10 +24,16 @@ public class MainViewModel : ViewModelBase
private readonly IAppDataService _appDataService;
private readonly IDirectoryService _directoryService;
private readonly IFileService _fileService;
private readonly IEncryptionService _encryptionService;
private readonly ITypeFactory _typeFactory;
private readonly IOpenFileService _openFileService;
private readonly IMessageService _messageService;

public MainViewModel(IScreenCaptureService screenCaptureService, ISystemInfoService systemInfoService,
IUIVisualizerService uiVisualizerService, IAppDataService appDataService,
IDirectoryService directoryService, IFileService fileService, IServiceProvider serviceProvider)
IDirectoryService directoryService, IFileService fileService, IServiceProvider serviceProvider,
IEncryptionService encryptionService, ITypeFactory typeFactory, IOpenFileService openFileService,
IMessageService messageService)
: base(serviceProvider)
{
_screenCaptureService = screenCaptureService;
Expand All @@ -36,12 +42,23 @@ public MainViewModel(IScreenCaptureService screenCaptureService, ISystemInfoServ
_appDataService = appDataService;
_directoryService = directoryService;
_fileService = fileService;
_encryptionService = encryptionService;
_typeFactory = typeFactory;
_openFileService = openFileService;
_messageService = messageService;

Screenshot = new TaskCommand(serviceProvider, OnScreenshotExecuteAsync);
ShowSystemInfo = new TaskCommand(serviceProvider, OnShowSystemInfoExecuteAsync);
SavePackage = new TaskCommand(serviceProvider, OnSavePackageExecuteAsync);
EncryptAndSavePackage = new TaskCommand(serviceProvider, OnEncryptAndSavePackageExecuteAsync);
GenerateKeys = new TaskCommand(serviceProvider, OnGenerateKeysExecuteAsync);
DecryptPackage = new TaskCommand(serviceProvider, OnDecryptPackageExecuteAsync);

Title = "Orc.SupportPackage example";

var currentDirectory = Environment.CurrentDirectory;
PublicKeyPath = Path.Combine(currentDirectory, "public.pem");
PrivateKeyPath = Path.Combine(currentDirectory, "private.pem");
}

public TaskCommand SavePackage { get; private set; }
Expand All @@ -51,6 +68,71 @@ private async Task OnSavePackageExecuteAsync()
await _uiVisualizerService.ShowDialogAsync<SupportPackageViewModel>();
}

public TaskCommand EncryptAndSavePackage { get; private set; }

private async Task OnEncryptAndSavePackageExecuteAsync()
{
var supportPackageViewModel = _typeFactory.CreateInstance<SupportPackageViewModel>();
supportPackageViewModel.EncryptionContext = new EncryptionContext
{
PrivateKeyPath = PrivateKeyPath,
PublicKey = await _encryptionService.ReadPublicKeyFromPemFileAsync(PublicKeyPath)
};

await _uiVisualizerService.ShowDialogAsync(supportPackageViewModel);
}

public TaskCommand GenerateKeys { get; private set; }

private async Task OnGenerateKeysExecuteAsync()
{
_encryptionService.Generate(PrivateKeyPath, PublicKeyPath);
await _messageService.ShowInformationAsync("Encryption keys generated");
}

public TaskCommand DecryptPackage { get; private set; }

private async Task OnDecryptPackageExecuteAsync()
{
var result = await _openFileService.DetermineFileAsync(new DetermineOpenFileContext
{
});

if (!result.Result || result.FileName is null)
{
return;
}

var directory = Path.GetDirectoryName(result.FileName);
var fileName = Path.GetFileNameWithoutExtension(result.FileName);

if (directory is null)
{
return;
}

var decryptedPackagePath = Path.Combine(directory, $"{fileName}_dec.spkg");

using (var sourceStream = _fileService.OpenRead(result.FileName))
{
if (_fileService.Exists(decryptedPackagePath))
{
_fileService.Delete(decryptedPackagePath);
}

using (var targetStream = _fileService.Create(decryptedPackagePath))
{
await _encryptionService.DecryptAsync(sourceStream, targetStream, new EncryptionContext
{
PrivateKeyPath = PrivateKeyPath,
PublicKey = await _encryptionService.ReadPublicKeyFromPemFileAsync(PublicKeyPath)
});
}
}

await _messageService.ShowInformationAsync($"Decrypted support package saved on path {decryptedPackagePath}");
}

public TaskCommand Screenshot { get; private set; }

private async Task OnScreenshotExecuteAsync()
Expand Down Expand Up @@ -91,4 +173,8 @@ private async Task OnShowSystemInfoExecuteAsync()
public BitmapImage ScreenPic { get; private set; }

public string SystemInfo { get; set; }

public string PrivateKeyPath { get; set; }

public string PublicKeyPath { get; set; }
}
48 changes: 47 additions & 1 deletion src/Orc.SupportPackage.Example/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,55 @@

<TabItem Header="Zipping all data">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<Label>Public Key</Label>
<Label Grid.Row="1">Private Key</Label>
<TextBox Grid.Column="1"
Margin="4"
Text="{Binding PublicKeyPath}" />
<TextBox Grid.Column="1"
Margin="4"
Grid.Row="1"
Text="{Binding PrivateKeyPath}" />

<Button Grid.RowSpan="2"
Grid.Column="2"
Margin="4"
Padding="6"
Command="{Binding GenerateKeys}">
Generate
</Button>

<Button Content="Save support package as"
Margin="15 15 15 15"
Margin="15"
Grid.ColumnSpan="3"
Grid.Row="2"
Command="{Binding SavePackage}" />

<Button Content="Save encrypted support package as"
Margin="15"
Grid.ColumnSpan="3"
Grid.Row="3"
Command="{Binding EncryptAndSavePackage}" />

<Button Content="Decrypt support package"
Margin="15"
Grid.ColumnSpan="3"
Grid.Row="4"
Command="{Binding DecryptPackage}" />
</Grid>
</TabItem>
</TabControl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,32 @@ namespace Orc
}
namespace Orc.SupportPackage
{
public class EncryptionContext
{
public EncryptionContext() { }
public EncryptionContext(Orc.SupportPackage.EncryptionContext context) { }
public EncryptionContext(string publicKey) { }
public string PrivateKeyPath { get; set; }
public string PublicKey { get; set; }
}
public class EncryptionService : Orc.SupportPackage.IEncryptionService
{
public EncryptionService(Orc.FileSystem.IFileService fileService) { }
public System.Threading.Tasks.Task DecryptAsync(System.IO.Stream sourceStream, System.IO.Stream targetStream, Orc.SupportPackage.EncryptionContext context) { }
public System.Threading.Tasks.Task EncryptAsync(System.IO.Stream sourceStream, System.IO.Stream targetStream, Orc.SupportPackage.EncryptionContext content) { }
public void Generate(string secretPath, string publicKeyPath) { }
public System.Threading.Tasks.Task<string> ReadPrivateKeyFromPemFileAsync(string keyPath) { }
public System.Threading.Tasks.Task<string> ReadPublicKeyFromPemFileAsync(string keyPath) { }
protected virtual System.Security.Cryptography.SymmetricAlgorithm CreateAlghorithm() { }
protected virtual byte[] GenerateSymmetricKey(int bits) { }
}
public interface IEncryptionService
{
System.Threading.Tasks.Task DecryptAsync(System.IO.Stream sourceStream, System.IO.Stream targetStream, Orc.SupportPackage.EncryptionContext content);
System.Threading.Tasks.Task EncryptAsync(System.IO.Stream sourceStream, System.IO.Stream targetStream, Orc.SupportPackage.EncryptionContext content);
void Generate(string secretLocation, string publicKeyLocation);
System.Threading.Tasks.Task<string> ReadPublicKeyFromPemFileAsync(string keyPath);
}
public interface IScreenCaptureService
{
System.Drawing.Image CaptureWindowImage(System.Windows.Window window);
Expand All @@ -33,6 +59,7 @@ namespace Orc.SupportPackage
public interface ISupportPackageService
{
System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string zipFileName, string[] directories, string[] excludeFileNamePatterns);
System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string zipFileName, string[] directories, string[] excludeFileNamePatterns, Orc.SupportPackage.EncryptionContext? encryptionContext);
}
public class ScreenCaptureService : Orc.SupportPackage.IScreenCaptureService
{
Expand All @@ -54,8 +81,9 @@ namespace Orc.SupportPackage
}
public class SupportPackageService : Orc.SupportPackage.ISupportPackageService
{
public SupportPackageService(Microsoft.Extensions.Logging.ILogger<Orc.SupportPackage.SupportPackageService> logger, Orc.SystemInfo.ISystemInfoService systemInfoService, Orc.SupportPackage.IScreenCaptureService screenCaptureService, Orc.FileSystem.IDirectoryService directoryService, Orc.FileSystem.IFileService fileService, Catel.Reflection.IEntryAssemblyResolver entryAssemblyResolver, Catel.Services.IAppDataService appDataService, System.Collections.Generic.IEnumerable<Orc.SupportPackage.ISupportPackageProvider> supportPackageProviders) { }
public SupportPackageService(Microsoft.Extensions.Logging.ILogger<Orc.SupportPackage.SupportPackageService> logger, Orc.SystemInfo.ISystemInfoService systemInfoService, Orc.SupportPackage.IScreenCaptureService screenCaptureService, Orc.FileSystem.IDirectoryService directoryService, Orc.FileSystem.IFileService fileService, Catel.Reflection.IEntryAssemblyResolver entryAssemblyResolver, Catel.Services.IAppDataService appDataService, System.Collections.Generic.IEnumerable<Orc.SupportPackage.ISupportPackageProvider> supportPackageProviders, Orc.SupportPackage.IEncryptionService encryptionService) { }
public virtual System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string zipFileName, string[] directories, string[] excludeFileNamePatterns) { }
public virtual System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string zipFileName, string[] directories, string[] excludeFileNamePatterns, Orc.SupportPackage.EncryptionContext? encryptionContext) { }
}
public static class ZipArchiveExtensions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace Orc.SupportPackage
public interface ISupportPackageBuilderService
{
System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string fileName, System.Collections.Generic.List<Orc.SupportPackage.SupportPackageFileSystemArtifact> artifacts);
System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string fileName, System.Collections.Generic.List<Orc.SupportPackage.SupportPackageFileSystemArtifact> artifacts, Orc.SupportPackage.EncryptionContext? encryptionContext);
}
public interface ISupportPackageContentProvider
{
Expand All @@ -41,6 +42,7 @@ namespace Orc.SupportPackage
{
public SupportPackageBuilderService(Orc.SupportPackage.ISupportPackageService supportPackageService, Orc.FileSystem.IFileService fileService) { }
public virtual System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string fileName, System.Collections.Generic.List<Orc.SupportPackage.SupportPackageFileSystemArtifact> artifacts) { }
public virtual System.Threading.Tasks.Task<bool> CreateSupportPackageAsync(string fileName, System.Collections.Generic.List<Orc.SupportPackage.SupportPackageFileSystemArtifact> artifacts, Orc.SupportPackage.EncryptionContext? encryptionContext) { }
}
public class SupportPackageDirectory : Orc.SupportPackage.SupportPackageFileSystemArtifact
{
Expand Down Expand Up @@ -70,6 +72,7 @@ namespace Orc.SupportPackage.ViewModels
public Catel.MVVM.TaskCommand AddFileCommand { get; }
public Catel.MVVM.TaskCommand CreateSupportPackage { get; }
public Catel.Collections.FastObservableCollection<string> CustomPaths { get; }
public Orc.SupportPackage.EncryptionContext? EncryptionContext { get; set; }
public bool IncludeCustomPathsInSupportPackage { get; set; }
public string? LastSupportPackageFileName { get; }
public Catel.MVVM.Command OpenDirectory { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
namespace Orc.SupportPackage;
namespace Orc.SupportPackage;

using System.Collections.Generic;
using System.Threading.Tasks;

public interface ISupportPackageBuilderService
{
Task<bool> CreateSupportPackageAsync(string fileName, List<SupportPackageFileSystemArtifact> artifacts);

Task<bool> CreateSupportPackageAsync(string fileName, List<SupportPackageFileSystemArtifact> artifacts, EncryptionContext? encryptionContext);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Orc.SupportPackage;
namespace Orc.SupportPackage;

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -28,7 +28,12 @@ public SupportPackageBuilderService(ISupportPackageService supportPackageService
_fileService = fileService;
}

public virtual async Task<bool> CreateSupportPackageAsync(string fileName, List<SupportPackageFileSystemArtifact> artifacts)
public virtual Task<bool> CreateSupportPackageAsync(string fileName, List<SupportPackageFileSystemArtifact> artifacts)
{
return CreateSupportPackageAsync(fileName, artifacts, null);
}

public virtual async Task<bool> CreateSupportPackageAsync(string fileName, List<SupportPackageFileSystemArtifact> artifacts, EncryptionContext? encryptionContext)
{
Argument.IsNotNullOrWhitespace(() => fileName);
ArgumentNullException.ThrowIfNull(artifacts);
Expand Down Expand Up @@ -66,7 +71,7 @@ public virtual async Task<bool> CreateSupportPackageAsync(string fileName, List<
builder.AppendLine("- " + directory);
}

var result = await _supportPackageService.CreateSupportPackageAsync(fileName, directories, excludeFileNamePatterns);
var result = await _supportPackageService.CreateSupportPackageAsync(fileName, directories, excludeFileNamePatterns, encryptionContext);

const string customDataDirectoryName = "CustomData";
await using var fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Orc.SupportPackage.ViewModels;
namespace Orc.SupportPackage.ViewModels;

using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -89,6 +89,13 @@ public SupportPackageViewModel(ISaveFileService saveFileService, ISupportPackage

public List<string> SelectedCustomPaths { get; }

/// <summary>
/// Optional encryption context. When set, the support package will be encrypted.
/// Applications can provide their own <see cref="IEncryptionService"/> implementation
/// via dependency injection to customize the encryption algorithm.
/// </summary>
public EncryptionContext? EncryptionContext { get; set; }

public TaskCommand AddDirectoryCommand { get; set; }

private async Task OnAddDirectoryExecuteAsync()
Expand Down Expand Up @@ -210,7 +217,7 @@ private async Task OnCreateSupportPackageExecuteAsync()
_isCreatingSupportPackage = false;
}))
{
await Task.Run(() => _supportPackageService.CreateSupportPackageAsync(fileName, supportPackageFileSystemArtifacts));
await Task.Run(() => _supportPackageService.CreateSupportPackageAsync(fileName, supportPackageFileSystemArtifacts, EncryptionContext));

LastSupportPackageFileName = fileName;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Orc.SupportPackage/Context/SupportPackageContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Orc.SupportPackage;
namespace Orc.SupportPackage;

using System;
using System.IO;
Expand Down
35 changes: 35 additions & 0 deletions src/Orc.SupportPackage/Cryptography/EncryptionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace Orc.SupportPackage
{
using Catel;

public class EncryptionContext
{
public EncryptionContext()
{

}

public EncryptionContext(EncryptionContext context)
{
Argument.IsNotNull(() => context);

PublicKey = context.PublicKey;
PrivateKeyPath = context.PrivateKeyPath;
}

public EncryptionContext(string publicKey)
{
PublicKey = publicKey;
}

/// <summary>
/// Base64-encrypted xml print of generated public key
/// </summary>
public string PublicKey { get; set; }

/// <summary>
/// Path to private key on disk
/// </summary>
public string PrivateKeyPath { get; set; }
}
}
Loading