diff --git a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertLoadResult.cs b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertLoadResult.cs
index a23a49d..1ce728d 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertLoadResult.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertLoadResult.cs
@@ -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);
}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertUtils.cs b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertUtils.cs
index 8445a69..f2ca99a 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertUtils.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertUtils.cs
@@ -14,7 +14,7 @@ internal static class CertUtils
/// - Message: an informational or error message
/// - RawContent: original file text or hex when parsing failed
///
- 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);
}
}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml
index 57a0e0b..83aa460 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml
+++ b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml
@@ -12,29 +12,41 @@
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml.cs
index 1df052a..732b6f8 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/CertViewerControl.xaml.cs
@@ -7,13 +7,43 @@ namespace QuickLook.Plugin.CertViewer;
public partial class CertViewerControl : UserControl, IDisposable
{
+ private string _currentPath;
+
public CertViewerControl()
{
InitializeComponent();
}
+ ///
+ /// 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.
+ ///
+ 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>
{
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>
{
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);
+ }
}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/Plugin.cs
index 1566caf..76509ac 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.CertViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.CertViewer/Plugin.cs
@@ -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);