Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
<Project>
<ItemGroup>
<PackageVersion Include="Azure.Identity" Version="1.21.0" />
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.9.0" />
<PackageVersion Include="Azure.Security.KeyVault.Keys" Version="4.9.0" />
<PackageVersion Include="Microsoft.Bcl.Cryptography" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.12" />
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.9.2" />
<PackageVersion Include="Azure.Security.KeyVault.Keys" Version="4.10.2" />
<PackageVersion Include="Microsoft.Bcl.Cryptography" Version="10.0.13" />
<PackageVersion Include="Microsoft.Extensions.FileSystemGlobbing" Version="10.0.13" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.13" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.13" />
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="10.0.13" />

<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Text.Json" Version="10.0.12" />
<PackageVersion Include="System.Text.Json" Version="10.0.13" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />

<PackageVersion Include="XenoAtom.CommandLine" Version="2.0.3" />
<PackageVersion Include="MinVer" Version="7.0.0" />
<PackageVersion Include="MinVer" Version="8.0.0" />

<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
<PackageVersion Include="NuGet.Packaging" Version="7.9.0" />
<PackageVersion Include="xunit.v3" Version="4.0.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.1" />
</ItemGroup>
</Project>
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,30 @@ This tool uses the same mechanisms for signing as the Windows SDK `signtool`. It
However, the formats that `azuresigntool` and `signtool` support vary by operating system and which Subject Interface Packages are
present on the system.

### NuGet Packages

NuGet packages (`.nupkg`) and symbol packages (`.snupkg`) are signed with a NuGet author signature, the same kind of signature
produced by `dotnet nuget sign`. Files are detected by their extension, so NuGet packages can be signed alongside other files in a
single invocation:

AzureSignTool.exe sign -kvu https://my-vault.vault.azure.net \
-kvm -kvc my-key-name \
-fd sha256 \
-tr http://timestamp.digicert.com -td sha256 \
C:\packages\*.nupkg

When signing NuGet packages:

* `--file-digest` and `--timestamp-digest` must be `sha256`, `sha384`, or `sha512`. SHA1 is not permitted.
* Only RFC 3161 timestamps (`--timestamp-rfc3161`) are supported. `--timestamp-authenticode` cannot be used.
* `--append-signature` is not supported. A package can only have one author signature, and any existing signature is replaced
unless `--skip-signed` is specified.
* `--description`, `--description-url`, `--page-hashing`, and `--no-page-hashing` have no effect.
* ZIP64 packages cannot be signed.

The certificate must meet the [NuGet signed package requirements](https://learn.microsoft.com/nuget/reference/signed-packages-reference),
such as having the code signing extended key usage, to be accepted by nuget.org. Signed packages can be verified with `dotnet nuget verify`.

## Exit Codes

The exit code is an HRESULT. Successfully signing produces a result of `S_OK` ("0"). If all files fail to sign, the exit code is
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
7 changes: 7 additions & 0 deletions src/AzureSign.Core/AlgorithmTranslator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Security.Cryptography;
using System.Text;

namespace AzureSign.Core
{
Expand Down Expand Up @@ -28,5 +29,11 @@ public static ReadOnlySpan<byte> HashAlgorithmToOidAsciiTerminated(HashAlgorithm
_ => throw new NotSupportedException("The algorithm specified is not supported."),
};
}

public static string HashAlgorithmToOid(HashAlgorithmName hashAlgorithmName)
{
ReadOnlySpan<byte> oid = HashAlgorithmToOidAsciiTerminated(hashAlgorithmName);
return Encoding.ASCII.GetString(oid.Slice(0, oid.Length - 1).ToArray());
}
}
}
15 changes: 1 addition & 14 deletions src/AzureSign.Core/AuthenticodeKeyVaultSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,8 @@ public AuthenticodeKeyVaultSigner(AsymmetricAlgorithm signingAlgorithm, X509Cert
_timeStampConfiguration = timeStampConfiguration ?? throw new ArgumentNullException(nameof(timeStampConfiguration));
_signingAlgorithm = signingAlgorithm ?? throw new ArgumentNullException(nameof(signingAlgorithm));
_certificateStore = MemoryCertificateStore.Create();
_chain = new X509Chain();
_chain = CertificateChainBuilder.Build(signingCertificate, additionalCertificates);

if (additionalCertificates is not null)
{
_chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
}

//We don't care about the trustworthiness of the cert. We just want a chain to sign with.
_chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;


if (!_chain.Build(signingCertificate))
{
throw new InvalidOperationException("Failed to build chain for certificate.");
}
for (var i = 0; i < _chain.ChainElements.Count; i++)
{
_certificateStore.Add(_chain.ChainElements[i].Certificate);
Expand Down
4 changes: 4 additions & 0 deletions src/AzureSign.Core/AzureSign.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@
</PackageReference>
</ItemGroup>

<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<PackageReference Include="System.Security.Cryptography.Pkcs" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions src/AzureSign.Core/CertificateChainBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Security.Cryptography.X509Certificates;

namespace AzureSign.Core
{
internal static class CertificateChainBuilder
{
public static X509Chain Build(X509Certificate2 certificate, X509Certificate2Collection? additionalCertificates)
{
X509Chain chain = new X509Chain();

try
{
if (additionalCertificates is not null)
{
chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates);
}

//We don't care about the trustworthiness of the cert. We just want a chain to sign with.
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;

if (!chain.Build(certificate))
{
throw new InvalidOperationException("Failed to build chain for certificate.");
}

return chain;
}
catch
{
chain.Dispose();
throw;
}
}
}
}
Loading