mirror of
https://github.com/QL-Win/QuickLook.git
synced 2026-01-14 07:06:15 +08:00
Add password support for protected certificates
Introduces UI and logic to handle password-protected certificate files. The CertViewerControl now prompts for a password if needed, and attempts to reload the certificate with the provided password. Refactored certificate loading flow to support this feature.
This commit is contained in:
@@ -8,15 +8,17 @@ internal sealed class CertLoadResult
|
||||
public X509Certificate2 Certificate { get; }
|
||||
public string Message { get; }
|
||||
public string RawContent { get; }
|
||||
public bool NeedsPassword { get; }
|
||||
|
||||
public CertLoadResult(bool success, X509Certificate2 certificate, string message, string rawContent)
|
||||
public CertLoadResult(bool success, X509Certificate2 certificate, string message, string rawContent, bool needsPassword = false)
|
||||
{
|
||||
Success = success;
|
||||
Certificate = certificate;
|
||||
Message = message;
|
||||
RawContent = rawContent;
|
||||
NeedsPassword = needsPassword;
|
||||
}
|
||||
|
||||
public static CertLoadResult From(bool success, X509Certificate2 certificate, string message, string rawContent)
|
||||
=> new CertLoadResult(success, certificate, message, rawContent);
|
||||
public static CertLoadResult From(bool success, X509Certificate2 certificate, string message, string rawContent, bool needsPassword = false)
|
||||
=> new CertLoadResult(success, certificate, message, rawContent, needsPassword);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ internal static class CertUtils
|
||||
/// - Message: an informational or error message
|
||||
/// - RawContent: original file text or hex when parsing failed
|
||||
/// </summary>
|
||||
public static CertLoadResult TryLoadCertificate(string path)
|
||||
public static CertLoadResult TryLoadCertificate(string path, string password = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -24,12 +24,16 @@ internal static class CertUtils
|
||||
{
|
||||
try
|
||||
{
|
||||
var cert = new X509Certificate2(path);
|
||||
var cert = !string.IsNullOrEmpty(password)
|
||||
? new X509Certificate2(path, password)
|
||||
: new X509Certificate2(path);
|
||||
return new CertLoadResult(true, cert, string.Empty, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new CertLoadResult(false, null, "Failed to load PFX/P12: " + ex.Message, null);
|
||||
var isPasswordError = ex is System.Security.Cryptography.CryptographicException ||
|
||||
(ex.Message?.IndexOf("password", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||
return new CertLoadResult(false, null, "Failed to load PFX/P12: " + ex.Message, null, isPasswordError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,29 +12,41 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DataGrid x:Name="PropertyList"
|
||||
Grid.Column="0"
|
||||
Margin="8"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column"
|
||||
RowHeaderWidth="0">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Field" Binding="{Binding Key}" Width="150" />
|
||||
<DataGridTextColumn Header="Value" Binding="{Binding Value}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<!-- Password panel (hidden by default) -->
|
||||
<StackPanel x:Name="PasswordPanel" Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal" Margin="8" Visibility="Collapsed" VerticalAlignment="Center">
|
||||
<TextBlock Text="This certificate appears to be password-protected. Enter password:" VerticalAlignment="Center" Margin="0,0,8,0" />
|
||||
<PasswordBox x:Name="InlinePasswordBox" Width="220" Margin="0,0,8,0" />
|
||||
<Button x:Name="LoadWithPasswordButton" Content="Load" Width="80" Margin="0,0,8,0" Click="LoadWithPasswordButton_Click" />
|
||||
<Button x:Name="CancelPasswordButton" Content="Cancel" Width="80" Click="CancelPasswordButton_Click" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox x:Name="RawText"
|
||||
Grid.Column="1"
|
||||
Margin="8"
|
||||
AcceptsReturn="True"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto" />
|
||||
<TabControl Grid.Row="1" Grid.ColumnSpan="2" Margin="8">
|
||||
<TabItem Header="Details">
|
||||
<DataGrid x:Name="PropertyList"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
HeadersVisibility="Column"
|
||||
RowHeaderWidth="0"
|
||||
Margin="0">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Field" Binding="{Binding Key}" Width="150" />
|
||||
<DataGridTextColumn Header="Value" Binding="{Binding Value}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
<TabItem Header="Raw">
|
||||
<TextBox x:Name="RawText"
|
||||
AcceptsReturn="True"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsReadOnly="True"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
Margin="0" />
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -7,13 +7,43 @@ namespace QuickLook.Plugin.CertViewer;
|
||||
|
||||
public partial class CertViewerControl : UserControl, IDisposable
|
||||
{
|
||||
private string _currentPath;
|
||||
|
||||
public CertViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a certificate file from path. If the file appears password-protected,
|
||||
/// the control will show an inline password input and allow the user to retry.
|
||||
/// </summary>
|
||||
public void LoadFromPath(string path)
|
||||
{
|
||||
_currentPath = path;
|
||||
|
||||
var result = CertUtils.TryLoadCertificate(path);
|
||||
|
||||
if (!result.Success && result.NeedsPassword)
|
||||
{
|
||||
// show password UI
|
||||
PasswordPanel.Visibility = System.Windows.Visibility.Visible;
|
||||
PropertyList.ItemsSource = null;
|
||||
RawText.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
PasswordPanel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
|
||||
if (result.Success && result.Certificate != null)
|
||||
LoadCertificate(result.Certificate);
|
||||
else
|
||||
LoadRaw(path, result.Message, result.RawContent);
|
||||
}
|
||||
|
||||
public void LoadCertificate(X509Certificate2 cert)
|
||||
{
|
||||
PasswordPanel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
var items = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Subject", cert.Subject),
|
||||
@@ -32,6 +62,7 @@ public partial class CertViewerControl : UserControl, IDisposable
|
||||
|
||||
public void LoadRaw(string path, string message, string content)
|
||||
{
|
||||
PasswordPanel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
PropertyList.ItemsSource = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new("Path", path),
|
||||
@@ -44,4 +75,34 @@ public partial class CertViewerControl : UserControl, IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private void LoadWithPasswordButton_Click(object sender, System.Windows.RoutedEventArgs e)
|
||||
{
|
||||
var pwd = InlinePasswordBox.Password;
|
||||
if (string.IsNullOrEmpty(_currentPath))
|
||||
return;
|
||||
|
||||
var result = CertUtils.TryLoadCertificate(_currentPath, pwd);
|
||||
PasswordPanel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
|
||||
if (result.Success && result.Certificate != null)
|
||||
{
|
||||
LoadCertificate(result.Certificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadRaw(_currentPath, result.Message, result.RawContent);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelPasswordButton_Click(object sender, System.Windows.RoutedEventArgs e)
|
||||
{
|
||||
// show failure/raw view
|
||||
if (string.IsNullOrEmpty(_currentPath))
|
||||
return;
|
||||
|
||||
var result = CertUtils.TryLoadCertificate(_currentPath);
|
||||
PasswordPanel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
LoadRaw(_currentPath, result.Message, result.RawContent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +56,8 @@ public class Plugin : IViewer
|
||||
|
||||
context.IsBusy = true;
|
||||
|
||||
var result = CertUtils.TryLoadCertificate(path);
|
||||
|
||||
_control = new CertViewerControl();
|
||||
|
||||
if (result.Success && result.Certificate != null)
|
||||
{
|
||||
_control.LoadCertificate(result.Certificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
_control.LoadRaw(path, result.Message, result.RawContent);
|
||||
}
|
||||
_control.LoadFromPath(path);
|
||||
|
||||
context.ViewerContent = _control;
|
||||
context.Title = Path.GetFileName(path);
|
||||
|
||||
Reference in New Issue
Block a user