Skip to content

Commit

Permalink
Merge pull request #716 from dlemstra/trusted-signing
Browse files Browse the repository at this point in the history
  • Loading branch information
clairernovotny committed Jun 24, 2024
2 parents db11f79 + 57ad13a commit 1e5a01d
Show file tree
Hide file tree
Showing 82 changed files with 2,291 additions and 498 deletions.
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Azure.Core" Version="1.38.0" />
<PackageVersion Include="Azure.CodeSigning.Sdk" Version="0.1.96" />
<PackageVersion Include="Azure.Identity" Version="1.11.4" />
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.6.0" />
<PackageVersion Include="AzureSign.Core" Version="4.0.1" />
Expand Down Expand Up @@ -31,4 +33,4 @@
<PackageVersion Include="System.Text.Json" Version="8.0.3" />
<PackageVersion Include="Microsoft.Dynamics.BusinessCentral.Sip.Main" Version="24.0.15760" />
</ItemGroup>
</Project>
</Project>
14 changes: 14 additions & 0 deletions sign.sln
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sign.SignatureProviders.Cer
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sign.SignatureProviders.CertificateStore.Test", "test\Sign.SignatureProviders.CertificateStore.Test\Sign.SignatureProviders.CertificateStore.Test.csproj", "{3AE48DC2-8422-4E3A-AFBC-12551D50DBCA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sign.SignatureProviders.TrustedSigning", "src\Sign.SignatureProviders.TrustedSigning\Sign.SignatureProviders.TrustedSigning.csproj", "{060800AF-42FC-493C-AD99-9C87212BA969}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sign.SignatureProviders.TrustedSigning.Test", "test\Sign.SignatureProviders.TrustedSigning.Test\Sign.SignatureProviders.TrustedSigning.Test.csproj", "{A81695AF-088A-436A-9A38-4D0B0DB2D826}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -84,6 +88,14 @@ Global
{3AE48DC2-8422-4E3A-AFBC-12551D50DBCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3AE48DC2-8422-4E3A-AFBC-12551D50DBCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3AE48DC2-8422-4E3A-AFBC-12551D50DBCA}.Release|Any CPU.Build.0 = Release|Any CPU
{060800AF-42FC-493C-AD99-9C87212BA969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{060800AF-42FC-493C-AD99-9C87212BA969}.Debug|Any CPU.Build.0 = Debug|Any CPU
{060800AF-42FC-493C-AD99-9C87212BA969}.Release|Any CPU.ActiveCfg = Release|Any CPU
{060800AF-42FC-493C-AD99-9C87212BA969}.Release|Any CPU.Build.0 = Release|Any CPU
{A81695AF-088A-436A-9A38-4D0B0DB2D826}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A81695AF-088A-436A-9A38-4D0B0DB2D826}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A81695AF-088A-436A-9A38-4D0B0DB2D826}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A81695AF-088A-436A-9A38-4D0B0DB2D826}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -98,6 +110,8 @@ Global
{47F03ADD-A646-44C8-92FA-9594CD4506E6} = {780818DD-6B52-47C8-AC54-71448DF822BD}
{68104303-9832-4841-89AB-B98712C4E618} = {92C73EE1-4EF3-4721-B6A9-9F458A673CA3}
{3AE48DC2-8422-4E3A-AFBC-12551D50DBCA} = {780818DD-6B52-47C8-AC54-71448DF822BD}
{060800AF-42FC-493C-AD99-9C87212BA969} = {92C73EE1-4EF3-4721-B6A9-9F458A673CA3}
{A81695AF-088A-436A-9A38-4D0B0DB2D826} = {780818DD-6B52-47C8-AC54-71448DF822BD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7AA1043F-37A2-404F-8EC3-34C747C1CEB7}
Expand Down
51 changes: 51 additions & 0 deletions src/Sign.Cli/AzureCredentialOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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.CommandLine;
using System.CommandLine.Invocation;
using System.CommandLine.IO;
using Azure.Core;
using Azure.Identity;

namespace Sign.Cli
{
internal sealed class AzureCredentialOptions
{
internal Option<bool?> ManagedIdentityOption { get; } = new(["-kvm", "--azure-key-vault-managed-identity"], Resources.ManagedIdentityOptionDescription);
internal Option<string?> TenantIdOption { get; } = new(["-kvt", "--azure-key-vault-tenant-id"], Resources.TenantIdOptionDescription);
internal Option<string?> ClientIdOption { get; } = new(["-kvi", "--azure-key-vault-client-id"], Resources.ClientIdOptionDescription);
internal Option<string?> ClientSecretOption { get; } = new(["-kvs", "--azure-key-vault-client-secret"], Resources.ClientSecretOptionDescription);

internal void AddOptionsToCommand(Command command)
{
command.AddOption(ManagedIdentityOption);
command.AddOption(TenantIdOption);
command.AddOption(ClientIdOption);
command.AddOption(ClientSecretOption);
}

internal TokenCredential? CreateTokenCredential(InvocationContext context)
{
bool? useManagedIdentity = context.ParseResult.GetValueForOption(ManagedIdentityOption);

if (useManagedIdentity is not null)
{
context.Console.Out.WriteLine(Resources.ManagedIdentityOptionObsolete);
}

string? tenantId = context.ParseResult.GetValueForOption(TenantIdOption);
string? clientId = context.ParseResult.GetValueForOption(ClientIdOption);
string? secret = context.ParseResult.GetValueForOption(ClientSecretOption);

if (!string.IsNullOrEmpty(tenantId) &&
!string.IsNullOrEmpty(clientId) &&
!string.IsNullOrEmpty(secret))
{
return new ClientSecretCredential(tenantId, clientId, secret);
}

return new DefaultAzureCredential();
}
}
}
55 changes: 10 additions & 45 deletions src/Sign.Cli/AzureKeyVaultCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,16 @@
using System.CommandLine.Invocation;
using System.CommandLine.IO;
using Azure.Core;
using Azure.Identity;
using Sign.Core;
using Sign.SignatureProviders.KeyVault;

namespace Sign.Cli
{
internal sealed class AzureKeyVaultCommand : Command
{
private readonly CodeCommand _codeCommand;

internal Option<string> CertificateOption { get; } = new(["-kvc", "--azure-key-vault-certificate"], AzureKeyVaultResources.CertificateOptionDescription);
internal Option<string?> ClientIdOption { get; } = new(["-kvi", "--azure-key-vault-client-id"], AzureKeyVaultResources.ClientIdOptionDescription);
internal Option<string?> ClientSecretOption { get; } = new(["-kvs", "--azure-key-vault-client-secret"], AzureKeyVaultResources.ClientSecretOptionDescription);
internal Option<bool> ManagedIdentityOption { get; } = new(["-kvm", "--azure-key-vault-managed-identity"], getDefaultValue: () => false, AzureKeyVaultResources.ManagedIdentityOptionDescription);
internal Option<string?> TenantIdOption { get; } = new(["-kvt", "--azure-key-vault-tenant-id"], AzureKeyVaultResources.TenantIdOptionDescription);
internal Option<Uri> UrlOption { get; } = new(["-kvu", "--azure-key-vault-url"], AzureKeyVaultResources.UrlOptionDescription);
internal Option<string> CertificateOption { get; } = new(["-kvc", "--azure-key-vault-certificate"], AzureKeyVaultResources.CertificateOptionDescription);
internal AzureCredentialOptions AzureCredentialOptions { get; } = new();

internal Argument<string?> FileArgument { get; } = new("file(s)", Resources.FilesArgumentDescription);

Expand All @@ -31,19 +25,12 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s
ArgumentNullException.ThrowIfNull(codeCommand, nameof(codeCommand));
ArgumentNullException.ThrowIfNull(serviceProviderFactory, nameof(serviceProviderFactory));

_codeCommand = codeCommand;

CertificateOption.IsRequired = true;
UrlOption.IsRequired = true;

ManagedIdentityOption.SetDefaultValue(false);

AddOption(UrlOption);
AddOption(TenantIdOption);
AddOption(ClientIdOption);
AddOption(ClientSecretOption);
AddOption(CertificateOption);
AddOption(ManagedIdentityOption);
AzureCredentialOptions.AddOptionsToCommand(this);

AddArgument(FileArgument);

Expand All @@ -67,41 +54,19 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s
return;
}
TokenCredential? credential = AzureCredentialOptions.CreateTokenCredential(context);
if (credential is null)
{
return;
}
// Some of the options are required and that is why we can safely use
// the null-forgiving operator (!) to simplify the code.
Uri url = context.ParseResult.GetValueForOption(UrlOption)!;
string? tenantId = context.ParseResult.GetValueForOption(TenantIdOption);
string? clientId = context.ParseResult.GetValueForOption(ClientIdOption);
string? secret = context.ParseResult.GetValueForOption(ClientSecretOption);
string certificateId = context.ParseResult.GetValueForOption(CertificateOption)!;
bool useManagedIdentity = context.ParseResult.GetValueForOption(ManagedIdentityOption);
TokenCredential? credential = null;
if (useManagedIdentity)
{
credential = new DefaultAzureCredential();
}
else
{
if (string.IsNullOrEmpty(tenantId) ||
string.IsNullOrEmpty(clientId) ||
string.IsNullOrEmpty(secret))
{
context.Console.Error.WriteFormattedLine(
AzureKeyVaultResources.InvalidClientSecretCredential,
TenantIdOption,
ClientIdOption,
ClientSecretOption);
context.ExitCode = ExitCode.NoInputsFound;
return;
}
credential = new ClientSecretCredential(tenantId, clientId, secret);
}
KeyVaultServiceProvider keyVaultServiceProvider = new(credential, url, certificateId);
await _codeCommand.HandleAsync(context, serviceProviderFactory, keyVaultServiceProvider, fileArgument);
await codeCommand.HandleAsync(context, serviceProviderFactory, keyVaultServiceProvider, fileArgument);
});
}
}
Expand Down
45 changes: 0 additions & 45 deletions src/Sign.Cli/AzureKeyVaultResources.Designer.cs

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

16 changes: 0 additions & 16 deletions src/Sign.Cli/AzureKeyVaultResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,9 @@
<data name="ClickOnceExtensionNotSupported" xml:space="preserve">
<value>ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation.</value>
</data>
<data name="ClientIdOptionDescription" xml:space="preserve">
<value>Client ID to authenticate to Azure Key Vault.</value>
</data>
<data name="ClientSecretOptionDescription" xml:space="preserve">
<value>Client secret to authenticate to Azure Key Vault.</value>
</data>
<data name="CommandDescription" xml:space="preserve">
<value>Use Azure Key Vault.</value>
</data>
<data name="InvalidClientSecretCredential" xml:space="preserve">
<value>If not using a managed identity, all of these options are required: {0}, {1}, and {2}.</value>
<comment>{NumberedPlaceholder="{0}", "{1}", "{2}"} are option names and should not be localized.</comment>
</data>
<data name="ManagedIdentityOptionDescription" xml:space="preserve">
<value>Managed identity to authenticate to Azure Key Vault.</value>
</data>
<data name="TenantIdOptionDescription" xml:space="preserve">
<value>Tenant ID to authenticate to Azure Key Vault.</value>
</data>
<data name="UrlOptionDescription" xml:space="preserve">
<value>URL to an Azure Key Vault.</value>
</data>
Expand Down
6 changes: 1 addition & 5 deletions src/Sign.Cli/CertificateStoreCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ namespace Sign.Cli
{
internal sealed class CertificateStoreCommand : Command
{
private readonly CodeCommand _codeCommand;

internal Option<string?> CertificateFingerprintOption { get; } = new(["-cfp", "--certificate-fingerprint"], ParseCertificateFingerprint, description: CertificateStoreResources.CertificateFingerprintOptionDescription);
internal Option<string?> CertificateFileOption { get; } = new(["-cf", "--certificate-file"], CertificateStoreResources.CertificateFileOptionDescription);
internal Option<string?> CertificatePasswordOption { get; } = new(["-p", "--password"], CertificateStoreResources.CertificatePasswordOptionDescription);
Expand All @@ -32,8 +30,6 @@ internal CertificateStoreCommand(CodeCommand codeCommand, IServiceProviderFactor
ArgumentNullException.ThrowIfNull(codeCommand, nameof(codeCommand));
ArgumentNullException.ThrowIfNull(serviceProviderFactory, nameof(serviceProviderFactory));

_codeCommand = codeCommand;

CertificateFingerprintOption.IsRequired = true;

AddOption(CertificateFingerprintOption);
Expand Down Expand Up @@ -111,7 +107,7 @@ internal CertificateStoreCommand(CodeCommand codeCommand, IServiceProviderFactor
certificatePassword,
useMachineKeyContainer);
await _codeCommand.HandleAsync(context, serviceProviderFactory, certificateStoreServiceProvider, fileArgument);
await codeCommand.HandleAsync(context, serviceProviderFactory, certificateStoreServiceProvider, fileArgument);
});
}

Expand Down
45 changes: 45 additions & 0 deletions src/Sign.Cli/Resources.Designer.cs

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

Loading

0 comments on commit 1e5a01d

Please sign in to comment.