Skip to content
Merged
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
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
<PackageVersion Include="System.Security.Cryptography.Pkcs" Version="7.0.3" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="7.0.1" />
<PackageVersion Include="System.Text.Json" Version="7.0.3" />
<PackageVersion Include="Microsoft.Dynamics.BusinessCentral.Sip.Main" Version="24.0.15760" />
</ItemGroup>
</Project>
18 changes: 17 additions & 1 deletion SdkTools.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" IncludeAssets="build" PrivateAssets="all" />
<PackageReference Include="Microsoft.Dynamics.BusinessCentral.Sip.Main" GeneratePathProperty="true" ExcludeAssets="All" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need a PrivateAssets="all"?

</ItemGroup>

<PropertyGroup>
Expand All @@ -23,6 +24,7 @@
<SdkFile64 Include="$(WindowsSDKBuildToolsBinVersionedFolder)\x64\wintrust.dll" />
<SdkFile64 Include="$(WindowsSDKBuildToolsBinVersionedFolder)\x64\wintrust.dll.ini" />
<SdkFile64 Include="$(WindowsSDKBuildToolsBinVersionedFolder)\x64\SignTool.exe.manifest" />
<SdkFile64 Include="$(PkgMicrosoft_Dynamics_BusinessCentral_Sip_Main)\x64\NavSip.dll" />

<SdkFile86 Include="$(NetSdkBinDir)\mage.exe" />
</ItemGroup>
Expand All @@ -32,8 +34,22 @@
<Copy SourceFiles="@(SdkFile86)" DestinationFolder="$(OutputPath)\tools\SDK\x86" SkipUnchangedFiles="true" />
</Target>

<Target Name="UpdateWintrust" AfterTargets="Build">
<PropertyGroup>
<PowerShellFilePath Condition=" '$(PowerShellFilePath)' == '' ">%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe</PowerShellFilePath>
<ScriptFilePath Condition=" '$(ScriptFilePath)' == '' ">$(RepositoryRootDirectory)\scripts\UpdateWintrust.ps1</ScriptFilePath>
</PropertyGroup>

<Exec Command="$(PowerShellFilePath) -NonInteractive -ExecutionPolicy Unrestricted -Command &quot;&amp; { &amp;'$(ScriptFilePath)' '$(OutputPath)\tools\SDK\x64\wintrust.dll.ini' } &quot;" LogStandardErrorAsError="true" />
</Target>

<ItemGroup>
<Content Include="@(SdkFile64)">
<Content Include="@(SdkFile64)" Exclude="$(WindowsSDKBuildToolsBinVersionedFolder)\x64\wintrust.dll.ini">
<Pack>true</Pack>
<PackagePath>tools\$(TargetFramework)\any\tools\SDK\x64</PackagePath>
<Visible>false</Visible>
</Content>
<Content Include="$(OutputPath)\tools\SDK\x64\wintrust.dll.ini">
<Pack>true</Pack>
<PackagePath>tools\$(TargetFramework)\any\tools\SDK\x64</PackagePath>
<Visible>false</Visible>
Expand Down
31 changes: 31 additions & 0 deletions scripts/UpdateWintrust.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
param(
[string] $WintrustIniPath
)

# Test if the Wintrust.ini file exists
if (!(Test-Path $WintrustIniPath -PathType Leaf)) {
exit 1
}

$wintrustini = Get-Content $WintrustIniPath -Raw

# Update the Wintrust.ini file to include the NavSIP.dll
$navSipIni = @'


[5]
DLL=NavSip.dll
GUID={36FFA03E-F824-48E7-8E07-4A2DCB034CC7}
CryptSIPDllCreateIndirectData=NavSIPCreateIndirectData
CryptSIPDllGetSignedDataMsg=NavSIPGetSignedDataMsg
CryptSIPDllIsMyFileType2=NavSIPIsFileSupportedName
CryptSIPDllPutSignedDataMsg=NavSIPPutSignedDataMsg
CryptSIPDllRemoveSignedDataMsg=NavSIPRemoveSignedDataMsg
CryptSIPDllVerifyIndirectData=NavSIPVerifyIndirectData
'@

if ($wintrustini -notmatch 'NavSip.dll') {
Write-Host "Adding NavSip.dll to wintrust.dll.ini - $WintrustIniPath"
$wintrustini += $navSipIni
Set-Content -Path $WintrustIniPath -Value $wintrustini
}
1 change: 1 addition & 0 deletions scripts/VerifyNuGetPackage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ If ($packageFilePaths.Count -ne 1)
'tools/net6.0/any/tools/SDK/x64/Microsoft.Windows.Build.Signing.mssign32.dll.manifest',
'tools/net6.0/any/tools/SDK/x64/Microsoft.Windows.Build.Signing.wintrust.dll.manifest',
'tools/net6.0/any/tools/SDK/x64/mssign32.dll',
'tools/net6.0/any/tools/SDK/x64/NavSip.dll',
'tools/net6.0/any/tools/SDK/x64/opcservices.dll',
'tools/net6.0/any/tools/SDK/x64/SignTool.exe.manifest',
'tools/net6.0/any/tools/SDK/x64/wintrust.dll',
Expand Down
1 change: 1 addition & 0 deletions src/Sign.Core/Sign.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<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
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public AzureSignToolSignatureProvider(

_supportedFileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
".app",
".appx",
".appxbundle",
".cab",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public void CanSign_WhenFileIsNull_Throws()
}

[Theory]
[InlineData(".app")]
[InlineData(".appx")]
[InlineData(".appxbundle")]
[InlineData(".cab")]
Expand Down
86 changes: 84 additions & 2 deletions test/Sign.Core.Test/SignerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public SignerTests(CertificatesFixture certificatesFixture)

_certificatesFixture = certificatesFixture;
_keyVaultServiceStub = new KeyVaultServiceStub();

RegisterSipsFromIniFile(); // Register SIPS from INI file to avoid relying SIPs being registered prior to running tests.
}

public void Dispose()
Expand Down Expand Up @@ -117,7 +119,21 @@ public async Task SignAsync_WhenFileIsMsixBundle_Signs()

await SignAsync(temporaryDirectory, file, outputFile);

await VerifyMsixBundleFile(outputFile, temporaryDirectory);
await VerifyMsixBundleFileAsync(outputFile, temporaryDirectory);
}
}

[Fact]
public async Task SignAsync_WhenFileIsApp_Signs()
{
using (TemporaryDirectory temporaryDirectory = new(new DirectoryService(Mock.Of<ILogger<IDirectoryService>>())))
{
FileInfo file = GetTestAsset(temporaryDirectory, "EmptyExtension.app");
FileInfo outputFile = new(Path.Combine(temporaryDirectory.Directory.FullName, "signed.app"));

await SignAsync(temporaryDirectory, file, outputFile);

await VerifyAppSignatureAsync(file, outputFile, temporaryDirectory);
}
}

Expand Down Expand Up @@ -167,7 +183,7 @@ private async Task VerifyAuthenticodeSignedFileAsync(FileInfo outputFile)
await VerifySignedCmsAsync(signedCms);
}

private async Task VerifyMsixBundleFile(FileInfo outputFile, TemporaryDirectory temporaryDirectory)
private async Task VerifyMsixBundleFileAsync(FileInfo outputFile, TemporaryDirectory temporaryDirectory)
{
using (FileStream fileStream = outputFile.OpenRead())
using (ZipArchive msixBundle = new(fileStream))
Expand Down Expand Up @@ -236,6 +252,52 @@ private static FileInfo ExtractEntry(TemporaryDirectory temporaryDirectory, ZipA
return file;
}

private async Task VerifyAppSignatureAsync(FileInfo unsignedAppFile, FileInfo signedAppFile, TemporaryDirectory temporaryDirectory)
{
FileInfo signatureFile = new(Path.Combine(temporaryDirectory.Directory.FullName, "signature.p7s"));

if (await TryExtractSignatureBlockAsync(unsignedAppFile, signedAppFile, signatureFile))
{
SignedCms signedCms = GetSignedCms(signatureFile);

await VerifySignedCmsAsync(signedCms);
}
else
{
Assert.Fail("The file is not signed.");
}
}

private static async Task<bool> TryExtractSignatureBlockAsync(
FileInfo unsignedAppFile,
FileInfo signedAppFile,
FileInfo signatureFile)
{
// NAVX signature block marker
ReadOnlyMemory<byte> nxsb = Encoding.UTF8.GetBytes("NXSB");

long endOfUnsignedApp = unsignedAppFile.Length;

using (BinaryReader reader = new(signedAppFile.OpenRead()))
{
reader.BaseStream.Seek(endOfUnsignedApp, SeekOrigin.Begin);

byte[] bytes = reader.ReadBytes(nxsb.Length);

if (nxsb.Span.SequenceEqual(bytes))
{
using (FileStream signatureStream = signatureFile.OpenWrite())
{
await reader.BaseStream.CopyToAsync(signatureStream);

return true;
}
}
}

return false;
}

private async Task VerifyAppxSignatureAsync(ZipArchive msix)
{
ZipArchiveEntry? entry = msix.GetEntry("AppxSignature.p7x");
Expand Down Expand Up @@ -295,6 +357,16 @@ private static SignedCms GetSignedCms(ZipArchiveEntry entry)
return signedCms;
}

private static SignedCms GetSignedCms(FileInfo file)
{
byte[] bytes = File.ReadAllBytes(file.FullName);
SignedCms signedCms = new();

signedCms.Decode(bytes);

return signedCms;
}

private static SignedCms GetSignedCmsFromBase64(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
Expand Down Expand Up @@ -436,5 +508,15 @@ private ServiceProvider Create()

return new ServiceProvider(services.BuildServiceProvider());
}

private static void RegisterSipsFromIniFile()
{
AppRootDirectoryLocator locator = new();
DirectoryInfo appRootDirectory = locator.Directory;
string baseDirectory = Path.Combine(appRootDirectory.FullName, "tools", "SDK", "x64");
Kernel32.SetDllDirectoryW(baseDirectory);
Kernel32.LoadLibraryW($@"{baseDirectory}\wintrust.dll");
Kernel32.LoadLibraryW($@"{baseDirectory}\mssign32.dll");
}
}
}
Binary file not shown.