Skip to content

Commit

Permalink
Add support for signing NuGet packages before publishing. (#70)
Browse files Browse the repository at this point in the history
Both Azure Key Vault and Azure Trusted Signing are supported as certificate sources, using 'dotnet sign' for the signing implementation.
  • Loading branch information
bgrainger authored Feb 14, 2025
1 parent 253fab4 commit 576996c
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/Faithlife.Build/AzureKeyVaultSigningSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Faithlife.Build;

/// <summary>
/// Settings for signing packages using Azure Key Vault.
/// </summary>
public sealed class AzureKeyVaultSigningSettings
{
/// <summary>
/// The Azure Key Vault URL.
/// </summary>
public Uri? KeyVaultUrl { get; set; }

/// <summary>
/// The Azure Key Vault certificate name.
/// </summary>
public string? CertificateName { get; set; }
}
38 changes: 38 additions & 0 deletions src/Faithlife.Build/DotNetBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -535,10 +535,48 @@ string[] GetTargetFrameworks()

var tagsToPush = new HashSet<string>();
var packageSettings = settings.PackageSettings;
List<string>? signingArguments = null;
var pushSuccess = false;

if (packagePaths.Count != 0 && packageSettings?.SigningSettings is { } signingSettings)
{
// build the arguments for 'dotnet sign'
signingArguments = ["sign", "code"];
switch (signingSettings)
{
case { AzureKeyVaultSettings: null, TrustedSigningSettings: null }:
throw new BuildException("Either TrustedSigningSettings or AzureKeyVaultSettings must be specified.");
case { AzureKeyVaultSettings: { } azureSettings, TrustedSigningSettings: null }:
signingArguments.AddRange([
"azure-key-vault",
"-kvu", azureSettings.KeyVaultUrl?.AbsoluteUri ?? throw new BuildException("SigningSettings.AzureKeyVaultSettings.KeyVaultUrl is required."),
"-kvc", azureSettings.CertificateName ?? throw new BuildException("SigningSettings.AzureKeyVaultSettings.CertificateName is required."),
]);
break;
case { AzureKeyVaultSettings: null, TrustedSigningSettings: { } trustedSettings }:
signingArguments.AddRange([
"trusted-signing",
"-tse", trustedSettings.EndpointUrl?.AbsoluteUri ?? throw new BuildException("SigningSettings.TrustedSigningSettings.EndpointUrl is required."),
"-tsa", trustedSettings.Account ?? throw new BuildException("SigningSettings.TrustedSigningSettings.Account is required."),
"-tscp", trustedSettings.CertificateProfile ?? throw new BuildException("SigningSettings.TrustedSigningSettings.CertificateProfile is required."),
]);
break;
default:
throw new BuildException("Only one of TrustedSigningSettings or AzureKeyVaultSettings can be specified.");
}

// install dotnet sign
RunDotNet("tool", "install", "--tool-path", "release/sign", "--prerelease", "sign");
}

foreach (var packagePath in packagePaths)
{
if (signingArguments is not null)
{
// sign the package before it's published; this will unzip it, sign each file it contains, rezip it, then sign the package as a whole
RunApp("release/sign/sign", [.. signingArguments, packagePath]);
}

var pushArgs = new[]
{
"nuget", "push", packagePath,
Expand Down
5 changes: 5 additions & 0 deletions src/Faithlife.Build/DotNetPackageSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ namespace Faithlife.Build;
/// </summary>
public sealed class DotNetPackageSettings
{
/// <summary>
/// Settings for signing NuGet packages.
/// </summary>
public DotNetSigningSettings? SigningSettings { get; set; }

/// <summary>
/// Called to find the projects to package.
/// </summary>
Expand Down
18 changes: 18 additions & 0 deletions src/Faithlife.Build/DotNetSigningSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Faithlife.Build;

/// <summary>
/// Settings for signing NuGet packages.
/// </summary>
/// <remarks>Only one of <see cref="TrustedSigningSettings"/> or <see cref="AzureKeyVaultSettings"/> must to specify the certificate to use for signing.</remarks>
public sealed class DotNetSigningSettings
{
/// <summary>
/// Settings for signing packages using Azure Trusted Signing.
/// </summary>
public TrustedSigningSettings? TrustedSigningSettings { get; set; }

/// <summary>
/// Settings for signing packages using a certificate stored in Azure Key Vault.
/// </summary>
public AzureKeyVaultSigningSettings? AzureKeyVaultSettings { get; set; }
}
23 changes: 23 additions & 0 deletions src/Faithlife.Build/TrustedSigningSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Faithlife.Build;

/// <summary>
/// Settings for signing packages using Azure Trusted Signing.
/// </summary>
/// <remarks>For more information, see <a href="https://learn.microsoft.com/en-us/azure/trusted-signing/overview">What is Trusted Signing?</a></remarks>
public sealed class TrustedSigningSettings
{
/// <summary>
/// The Trusted Signing Account endpoint. The value must be a URI that aligns to the region that your Trusted Signing Account and Certificate Profile were created in.
/// </summary>
public Uri? EndpointUrl { get; set; }

/// <summary>
/// The Trusted Signing Account name.
/// </summary>
public string? Account { get; set; }

/// <summary>
/// The Certificate Profile name.
/// </summary>
public string? CertificateProfile { get; set; }
}

0 comments on commit 576996c

Please sign in to comment.