Skip to content

Commit

Permalink
exporter: add IExporter and Exporter impl
Browse files Browse the repository at this point in the history
Add new 'export' component that can export a certificate to a file on
disk from a certificate provider.
  • Loading branch information
mjcheetham committed Jun 26, 2024
1 parent 1df56d5 commit 262104a
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 2 deletions.
72 changes: 72 additions & 0 deletions src/Sign.Core/Exporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE.txt file in the project root for more information.

using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Sign.Core;

internal class Exporter : IExporter
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<IExporter> _logger;

// Dependency injection requires a public constructor.
public Exporter(IServiceProvider serviceProvider, ILogger<IExporter> logger)
{
ArgumentNullException.ThrowIfNull(serviceProvider, nameof(serviceProvider));
ArgumentNullException.ThrowIfNull(logger, nameof(logger));

_serviceProvider = serviceProvider;
_logger = logger;
}

public async Task<int> ExportAsync(string outputPath)
{
try
{
ICertificateProvider certificateProvider = _serviceProvider.GetRequiredService<ICertificateProvider>();
using (X509Certificate2 certificate = await certificateProvider.GetCertificateAsync())
{
_logger.LogInformation(Resources.FetchedCertificateFingerprint, certificate.Thumbprint);

// Write out copy of public part of certificate if an output path was given
if (!string.IsNullOrWhiteSpace(outputPath))
{
// Ensure parent directory exists
string? parentDir = Path.GetDirectoryName(outputPath);
if (parentDir is not null && !Directory.Exists(parentDir))
{
_logger.LogDebug(Resources.CreatingDirectory, parentDir);
Directory.CreateDirectory(parentDir);
}

// Clean up any existing file
if (File.Exists(outputPath))
{
_logger.LogDebug(Resources.DeletingFile, outputPath);
File.Delete(outputPath);
_logger.LogDebug(Resources.DeletedFile, outputPath);
}

_logger.LogInformation(Resources.ExportingCertificate, outputPath);

var sw = Stopwatch.StartNew();
byte[] data = certificate.Export(X509ContentType.Cert);
await File.WriteAllBytesAsync(outputPath, data);
_logger.LogInformation(Resources.ExportSucceededWithTimeElapsed, sw.ElapsedMilliseconds);
}
}
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
return ExitCode.Failed;
}

return ExitCode.Success;
}
}
10 changes: 10 additions & 0 deletions src/Sign.Core/IExporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE.txt file in the project root for more information.

namespace Sign.Core;

internal interface IExporter
{
Task<int> ExportAsync(string outputPath);
}
45 changes: 45 additions & 0 deletions src/Sign.Core/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion src/Sign.Core/Resources.resx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Expand Down Expand Up @@ -168,6 +168,14 @@
<value>Deleting directory {path}.</value>
<comment>{Placeholder="{path}"} is a directory path.</comment>
</data>
<data name="DeletedFile" xml:space="preserve">
<value>File {path} deleted.</value>
<comment>{Placeholder="{path}"} is a file path.</comment>
</data>
<data name="DeletingFile" xml:space="preserve">
<value>Deleting file {path}.</value>
<comment>{Placeholder="{path}"} is a file path.</comment>
</data>
<data name="DirectoryNotDeleted" xml:space="preserve">
<value>Directory {path} still exists.</value>
<comment>{Placeholder="{path}"} is a directory path.</comment>
Expand All @@ -183,10 +191,22 @@
<value>An exception occurred while attempting to delete directory {path}.</value>
<comment>{Placeholder="{path}"} is a directory path.</comment>
</data>
<data name="ExportingCertificate" xml:space="preserve">
<value>Exporting certificate to {path}.</value>
<comment>{Placeholder="{path}"} is a path to a certificate file.</comment>
</data>
<data name="ExportSucceededWithTimeElapsed" xml:space="preserve">
<value>Completed in {millseconds} ms.</value>
<comment>{Placeholder="{milliseconds}"} is a decimal number representing the number of milliseconds elapsed, and "ms" is the unit abbreviation for milliseconds.</comment>
</data>
<data name="FetchedCertificate" xml:space="preserve">
<value>Fetched certificate. [{milliseconds} ms]</value>
<comment>{Placeholder="{milliseconds}"} is a decimal number representing the number of milliseconds elapsed, and "ms" is the unit abbreviation for milliseconds.</comment>
</data>
<data name="FetchedCertificateFingerprint" xml:space="preserve">
<value>Fetched certificate with fingerprint {fingerprint}.</value>
<comment>{Placeholder="{fingerprint}"} is an identifier for a certificate.</comment>
</data>
<data name="FetchingCertificate" xml:space="preserve">
<value>Fetching certificate from Azure Key Vault.</value>
</data>
Expand Down
3 changes: 2 additions & 1 deletion src/Sign.Core/ServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ internal static ServiceProvider CreateDefault(
services.AddSingleton<IVsixSignTool, VsixSignTool>();
services.AddSingleton<ICertificateVerifier, CertificateVerifier>();
services.AddSingleton<ISigner, Signer>();
services.AddSingleton<IExporter, Exporter>();

if (addServices is not null)
{
Expand All @@ -85,4 +86,4 @@ internal static ServiceProvider CreateDefault(
return new ServiceProvider(services.BuildServiceProvider());
}
}
}
}

0 comments on commit 262104a

Please sign in to comment.