Skip to content

Commit

Permalink
Use Azure Key Vault's RSA implementation (#750)
Browse files Browse the repository at this point in the history
Resolve #649
  • Loading branch information
dtivel authored Aug 6, 2024
1 parent 74cd61f commit 6584f5d
Show file tree
Hide file tree
Showing 9 changed files with 466 additions and 31 deletions.
6 changes: 0 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
<PackageVersion Include="Azure.CodeSigning.Sdk" Version="0.1.106" />
<PackageVersion Include="Azure.Core" Version="1.42.0" />
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
<!-- Lift this dependency from NuGetKeyVaultSignTool.Core to get the latest version. -->
<PackageVersion Include="Azure.Security.KeyVault.Certificates" Version="4.6.0" />
<!-- Lift this dependency from NuGetKeyVaultSignTool.Core to get the latest version. -->
<PackageVersion Include="Azure.Security.KeyVault.Keys" Version="4.6.0" />
<PackageVersion Include="AzureSign.Core" Version="4.0.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
Expand All @@ -24,12 +22,8 @@
<!-- Only use release versions. Pre-release versions are signed with an untrusted certificate. -->
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1" />
<PackageVersion Include="Moq" Version="4.18.4" />
<!-- Lift this dependency from NuGetKeyVaultSignTool.Core to get the latest version. -->
<PackageVersion Include="NuGet.Packaging" Version="6.10.1" />
<!-- Lift this dependency from NuGetKeyVaultSignTool.Core to get the latest version. -->
<PackageVersion Include="NuGet.Protocol" Version="6.10.1" />
<PackageVersion Include="NuGetKeyVaultSignTool.Core" Version="3.2.3" />
<PackageVersion Include="RSAKeyVaultProvider" Version="2.1.1" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="8.0.1" />
Expand Down
30 changes: 29 additions & 1 deletion THIRD-PARTY-NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,32 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Available at https://github.com/vcsjones/OpenOpcSignTool/blob/main/LICENSE
Available at https://github.com/vcsjones/OpenOpcSignTool/blob/main/LICENSE


License notice for NuGetKeyVaultSignTool
-------------------------------

The MIT License (MIT)

Copyright (c) Claire Novotny

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Available at https://github.com/novotnyllc/NuGetKeyVaultSignTool/blob/main/LICENSE
5 changes: 1 addition & 4 deletions src/Sign.Core/Sign.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Security.KeyVault.Keys" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="Azure.Security.KeyVault.Certificates" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="AzureSign.Core" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="Microsoft.Dynamics.BusinessCentral.Sip.Main" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="NuGet.Packaging" />
<PackageReference Include="NuGet.Protocol" />
<PackageReference Include="NuGetKeyVaultSignTool.Core" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="System.Security.Cryptography.Pkcs" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="System.Security.Cryptography.Xml" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="System.Text.Json" PrivateAssets="analyzers;build;compile;contentfiles" />
<PackageReference Include="Microsoft.Dynamics.BusinessCentral.Sip.Main" />
</ItemGroup>

<ItemGroup>
Expand Down
98 changes: 98 additions & 0 deletions src/Sign.Core/Tools/NuGet/NuGetLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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 Microsoft.Extensions.Logging;
using NuGet.Common;
using LogLevel = NuGet.Common.LogLevel;

namespace Sign.Core
{
internal sealed class NuGetLogger : NuGet.Common.ILogger
{
private readonly Microsoft.Extensions.Logging.ILogger _logger;
private readonly string _fileName;

internal NuGetLogger(Microsoft.Extensions.Logging.ILogger logger, string fileName)
{
ArgumentNullException.ThrowIfNull(logger, nameof(logger));
ArgumentException.ThrowIfNullOrEmpty(fileName, nameof(fileName));

_logger = logger;
_fileName = fileName;
}

public void Log(LogLevel level, string data)
{
_logger.Log(ConvertLevel(level), $"NuGet [{_fileName}]: {data}");
}

public void Log(ILogMessage message)
{
Log(message.Level, message.FormatWithCode());
}

public Task LogAsync(LogLevel level, string data)
{
Log(level, data);

return Task.CompletedTask;
}

public Task LogAsync(ILogMessage message)
{
Log(message.Level, message.FormatWithCode());

return Task.CompletedTask;
}

public void LogDebug(string data)
{
Log(LogLevel.Debug, data);
}

public void LogError(string data)
{
Log(LogLevel.Error, data);
}

public void LogInformation(string data)
{
Log(LogLevel.Information, data);
}

public void LogInformationSummary(string data)
{
Log(LogLevel.Information, data);
}

public void LogMinimal(string data)
{
Log(LogLevel.Minimal, data);
}

public void LogVerbose(string data)
{
Log(LogLevel.Verbose, data);
}

public void LogWarning(string data)
{
Log(LogLevel.Warning, data);
}

private static Microsoft.Extensions.Logging.LogLevel ConvertLevel(LogLevel level)
{
return level switch
{
LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug,
LogLevel.Verbose => Microsoft.Extensions.Logging.LogLevel.Trace,
LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information,
LogLevel.Minimal => Microsoft.Extensions.Logging.LogLevel.Information,
LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning,
LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error,
_ => Microsoft.Extensions.Logging.LogLevel.Information
};
}
}
}
138 changes: 138 additions & 0 deletions src/Sign.Core/Tools/NuGet/NuGetPackageSigner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Logging;
using NuGet.Common;
using NuGet.Packaging.Signing;
using NuGet.Protocol;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace Sign.Core
{
internal sealed class NuGetPackageSigner
{
private readonly ILogger _logger;

public NuGetPackageSigner(ILogger logger)
{
ArgumentNullException.ThrowIfNull(logger, nameof(logger));

_logger = logger;
}

public async Task<bool> SignAsync(
string packagePath,
string outputPath,
Uri timestampUrl,
SignatureType signatureType,
HashAlgorithmName signatureHashAlgorithm,
HashAlgorithmName timestampHashAlgorithm,
X509Certificate2 signingCertificate,
System.Security.Cryptography.RSA rsa,
bool overwrite,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(packagePath, nameof(packagePath));
ArgumentException.ThrowIfNullOrEmpty(outputPath, nameof(outputPath));
ArgumentNullException.ThrowIfNull(timestampUrl, nameof(timestampUrl));
ArgumentNullException.ThrowIfNull(signingCertificate, nameof(signingCertificate));
ArgumentNullException.ThrowIfNull(rsa, nameof(rsa));

bool inPlaceSigning = String.Equals(packagePath, outputPath);
bool usingWildCards = packagePath.Contains('*') || packagePath.Contains('?');
IEnumerable<string> packageFilePaths = LocalFolderUtility.ResolvePackageFromPath(packagePath);
NuGetSignatureProvider signatureProvider = new(rsa, new Rfc3161TimestampProvider(timestampUrl));
SignPackageRequest? request = null;

if (signatureType == SignatureType.Author)
{
request = new AuthorSignPackageRequest(signingCertificate, signatureHashAlgorithm, timestampHashAlgorithm);
}
else
{
throw new NotSupportedException(nameof(signatureType));
}

foreach (string packageFilePath in packageFilePaths)
{
cancellationToken.ThrowIfCancellationRequested();

string packageFileName = Path.GetFileName(packageFilePath);

_logger.LogInformation($"{nameof(SignAsync)} [{packageFilePath}]: Begin signing {packageFileName}");

string? originalPackageCopyPath = null;

try
{
originalPackageCopyPath = CopyPackage(packageFilePath);
string signedPackagePath = outputPath;

if (inPlaceSigning)
{
signedPackagePath = packageFilePath;
}
else if (usingWildCards)
{
string? pathName = Path.GetDirectoryName(outputPath + Path.DirectorySeparatorChar);

if (!string.IsNullOrEmpty(pathName) && !Directory.Exists(pathName))
{
Directory.CreateDirectory(pathName);
}

signedPackagePath = pathName + Path.DirectorySeparatorChar + packageFileName;
}

using (SigningOptions options = SigningOptions.CreateFromFilePaths(
originalPackageCopyPath,
signedPackagePath,
overwrite,
signatureProvider,
new NuGetLogger(_logger, packageFilePath)))
{
await SigningUtility.SignAsync(options, request, cancellationToken);
}
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
return false;
}
finally
{
if (!string.IsNullOrEmpty(originalPackageCopyPath))
{
try
{
FileUtility.Delete(originalPackageCopyPath);
}
catch
{
}
}

_logger.LogInformation($"{nameof(SignAsync)} [{packageFilePath}]: End signing {packageFileName}");
}
}

return true;
}

private static string CopyPackage(string sourceFilePath)
{
string destFilePath = Path.GetTempFileName();

File.Copy(sourceFilePath, destFilePath, overwrite: true);

return destFilePath;
}

private static void OverwritePackage(string sourceFilePath, string destFilePath)
{
File.Copy(sourceFilePath, destFilePath, overwrite: true);
}
}
}
Loading

0 comments on commit 6584f5d

Please sign in to comment.