Azure Trusted Signing fails when using Public Trust test certificates
Describe the bug When using Azure Trusted Signing with a Public Trust test certificate, the signing fails. Using a non-testing certificate from the same Trusted Signing Account works as intended. Based on my own testing this has worked at least ~6 months ago. I tested older versions of dotnet sign and it does not work with those either.
What I haven't done is testing with .NET 8.
Repro steps
I created a simple console app and tried to sign the exe with the following command:
sign code trusted-signing -tse https://weu.codesigning.azure.net/ -tsa trusted-signing-account -tscp signing-profile-test .\*.exe -v trace
Expected behavior The files should be signed successfully.
Actual behavior
Dotnet sign fails with error Failed to build chain for certificate.
Additional context
- Include the output of
sign --version. - Include the output of
dotnet --info. - Add any other context about the problem here.
PS C:\Users\user\sources\ConsoleApp1\bin\Debug> sign code trusted-signing -tse https://weu.codesigning.azure.net/ -tsa trusted-signing-account -tscp signing-profile-test .\*.exe -v trace
trce: Sign.SignatureProviders.TrustedSigning.TrustedSigningService[0]
Fetching certificate from Trusted Signing certificate profile.
trce: Sign.SignatureProviders.TrustedSigning.TrustedSigningService[0]
Fetched certificate. [2368.8501 ms]
trce: Sign.SignatureProviders.TrustedSigning.TrustedSigningService[0]
Certificate details:
[Version]
V3
[Subject]
CN=Organization name(TEST ONLY), O=Organization name, L=Country, S=Region, C=C
Simple Name: Organization name(TEST ONLY)
DNS Name: Organization name(TEST ONLY)
[Issuer]
CN=Microsoft Developer ID Test CS AOC CA 02, O=Microsoft Corporation, C=US
Simple Name: Microsoft Developer ID Test CS AOC CA 02
DNS Name: Microsoft Developer ID Test CS AOC CA 02
[Serial Number]
330024D2252D0A7B1DB4C7150200010024D225
[Not Before]
21/07/2025 4.56.37
[Not After]
24/07/2025 4.56.37
[Thumbprint]
8E7B8068F369046F8572C63B27D02CB31971FC71
[Signature Algorithm]
sha384RSA(1.2.840.113549.1.1.12)
[Public Key]
Algorithm: RSA
Length: 3072
Key Blob: 30 82 01 8a 02 82 01 81 00 9a c2 28 2a bc 4a 32 d3 0c 3b e1 7e cf 93 11 73 70 11 a2 2b a8 35 81 03 64 55 70 99 2f 9b 0d 8e 77 c5 1e 52 51 13 55 09 0c f5 f4 bf 8a 53 b1 e2 26 cb 8d bb b2 15 ad a8 40 da 5e a6 bc d2 e9 ab 22 4e 1e 78 14 1b c7 3a ca 38 74 a7 5b f6 6f 7b 4f ad 99 cc 7c f9 56 79 da 38 f2 b0 65 3d 0a 8a b8 cc 37 09 03 7e 94 1c 4c 21 c4 7f 7e 0b 0e cf 2d 48 4e a7 42 fd fc 5b 40 66 2d 90 23 05 04 20 6f 0a 38 a1 ba d1 13 c8 11 54 07 b9 96 e8 34 2a 77 e9 02 e5 ce 98 22 97 f4 26 17 cf 23 75 f0 49 bb d9 10 ac b5 15 e6 80 50 b1 1e 97 e9 aa 20 2f cc 7a f2 53 a0 12 f3 a5 c9 b0 66 93 1c ff c6 6d 11 d6 0c ab 3a 01 a4 c5 8e b9 a8 69 0f 02 36 74 67 90 c0 ac 41 20 10 8e 72 43 73 0c 84 02 84 ac a0 2d e2 0f c0 ac 44 eb d9 ae 7b e6 59 6d 57 80 bd e1 d1 84 2b a9 3d 7a cd 46 d0 79 e5 85 88 e0 fb aa 43 c7 fe c8 10 85 29 77 ef 84 42 6f ec 9f 5d 1c ef 97 2e 5e 6a 56 8b f0 73 5e f5 83 96 87 75 93 fa ae 1b 1d 75 1c 60 89 9c fd cd 0e a3 d2 ff b0 ee dd 86 1c 4d 83 6c 44 3e 62 59 b3 2c 60 93 4d 5c 9a c8 65 ed ec 7a 90 12 de 55 4b fa 8c 81 aa ec 31 65 74 1c c1 4a 13 2f 18 a6 09 35 cb 76 7e 71 df 9d 7e f2 a7 7b 94 5a 58 5b 01 c4 8f c9 f6 37 91 49 e6 b1 99 af 50 17 fc 1e 13 c5 cd 02 03 01 00 01
Parameters: 05 00
[Extensions]
* Basic Constraints(2.5.29.19):
Subject Type=End Entity
Path Length Constraint=None
* Key Usage(2.5.29.15):
Digital Signature (80)
* Enhanced Key Usage(2.5.29.37):
Code Signing (1.3.6.1.5.5.7.3.3)
Lifetime Signing (1.3.6.1.4.1.311.10.3.13)
Unknown Key Usage (1.3.6.1.4.1.311.97.1.2.740251510.862486973.67201376.284453507)
* Subject Key Identifier(2.5.29.14):
9651bcda79bbe09dc0e160804855529f2159bef0
* Authority Key Identifier(2.5.29.35):
KeyID=cf9019e7d9132a69bd980868f94c7802cff96f48
* CRL Distribution Points(2.5.29.31):
[1]CRL Distribution Point
Distribution Point Name:
Full Name:
URL=http://www.microsoft.com/pkiops/crl/Microsoft Developer ID Test CS AOC CA 02.crl (http://www.microsoft.com/pkiops/crl/Microsoft%20Developer%20ID%20Test%20CS%20AOC%20CA%2002.crl)
* Authority Information Access(1.3.6.1.5.5.7.1.1):
[1]Authority Info Access
Access Method=Certification Authority Issuer (1.3.6.1.5.5.7.48.2)
Alternative Name:
URL=http://www.microsoft.com/pkiops/certs/Microsoft Developer ID Test CS AOC CA 02(1).crt (http://www.microsoft.com/pkiops/certs/Microsoft%20Developer%20ID%20Test%20CS%20AOC%20CA%2002(1).crt)
info: Sign.Core.ISigner[0]
Submitting C:\Users\user\sources\ConsoleApp1\bin\Debug\ConsoleApp1.exe for signing.
trce: Sign.Core.IDirectoryService[0]
Creating directory C:\Users\user\AppData\Local\Temp\vvrcejya.m4p.
info: Sign.Core.ISigner[0]
SignAsync called for C:\Users\user\sources\ConsoleApp1\bin\Debug\ConsoleApp1.exe. Using C:\Users\user\AppData\Local\Temp\vvrcejya.m4p\mxng0pkc.exe locally.
info: Sign.Core.IDataFormatSigner[0]
Signing SignTool job with 1 files.
trce: Sign.Core.IDirectoryService[0]
Deleting directory C:\Users\user\AppData\Local\Temp\vvrcejya.m4p.
trce: Sign.Core.IDirectoryService[0]
Directory C:\Users\user\AppData\Local\Temp\vvrcejya.m4p deleted.
fail: Sign.Core.ISigner[0]
Failed to build chain for certificate.
System.InvalidOperationException: Failed to build chain for certificate.
at AzureSign.Core.AuthenticodeKeyVaultSigner..ctor(AsymmetricAlgorithm signingAlgorithm, X509Certificate2 signingCertificate, HashAlgorithmName fileDigestAlgorithm, TimeStampConfiguration timeStampConfiguration, X509Certificate2Collection additionalCertificates)
at Sign.Core.AzureSignToolSigner.SignAsync(IEnumerable`1 files, SignOptions options) in /_/src/Sign.Core/DataFormatSigners/AzureSignToolSigner.cs:line 110
at Sign.Core.AggregatingSigner.SignAsync(IEnumerable`1 files, SignOptions options) in /_/src/Sign.Core/DataFormatSigners/AggregatingSigner.cs:line 92
at Sign.Core.Signer.<>c__DisplayClass3_0.<<SignAsync>b__0>d.MoveNext() in /_/src/Sign.Core/Signer.cs:line 157
--- End of stack trace from previous location ---
at System.Threading.Tasks.Parallel.<>c__53`1.<<ForEachAsync>b__53_0>d.MoveNext()
--- End of stack trace from previous location ---
at Sign.Core.Signer.SignAsync(IReadOnlyList`1 inputFiles, String outputFile, FileInfo fileList, Boolean recurseContainers, DirectoryInfo baseDirectory, String applicationName, String publisherName, String description, Uri descriptionUrl, Uri timestampUrl, Int32 maxConcurrency, HashAlgorithmName fileHashAlgorithm, HashAlgorithmName timestampHashAlgorithm) in /_/src/Sign.Core/Signer.cs:line 86
Sign version and dotnet info:
PS C:\Users\user\sources\ConsoleApp1\bin\Debug> sign --version
0.9.1-beta.25330.2+dc01dca32471b368ad640358778e172d1bd249f9
PS C:\Users\user\sources\ConsoleApp1\bin\Debug> dotnet --info
.NET SDK:
Version: 9.0.103
Commit: 96da45d427
Workload version: 9.0.100-manifests.ea610b94
MSBuild version: 17.12.24+90b52dda6
Runtime Environment:
OS Name: Windows
OS Version: 10.0.22631
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\9.0.103\
.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.
Host:
Version: 9.0.2
Architecture: x64
Commit: 80aa709f5d
.NET SDKs installed:
9.0.103 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
[Microsoft.AspNetCore.App](http://microsoft.aspnetcore.app/) 6.0.36 [C:\Program Files\dotnet\shared\[Microsoft.AspNetCore.App](http://microsoft.aspnetcore.app/)]
[Microsoft.AspNetCore.App](http://microsoft.aspnetcore.app/) 8.0.13 [C:\Program Files\dotnet\shared\[Microsoft.AspNetCore.App](http://microsoft.aspnetcore.app/)]
[Microsoft.AspNetCore.App](http://microsoft.aspnetcore.app/) 9.0.2 [C:\Program Files\dotnet\shared\[Microsoft.AspNetCore.App](http://microsoft.aspnetcore.app/)]
[Microsoft.NETCore.App](http://microsoft.netcore.app/) 6.0.36 [C:\Program Files\dotnet\shared\[Microsoft.NETCore.App](http://microsoft.netcore.app/)]
[Microsoft.NETCore.App](http://microsoft.netcore.app/) 8.0.13 [C:\Program Files\dotnet\shared\[Microsoft.NETCore.App](http://microsoft.netcore.app/)]
[Microsoft.NETCore.App](http://microsoft.netcore.app/) 9.0.2 [C:\Program Files\dotnet\shared\[Microsoft.NETCore.App](http://microsoft.netcore.app/)]
[Microsoft.WindowsDesktop.App](http://microsoft.windowsdesktop.app/) 6.0.36 [C:\Program Files\dotnet\shared\[Microsoft.WindowsDesktop.App](http://microsoft.windowsdesktop.app/)]
[Microsoft.WindowsDesktop.App](http://microsoft.windowsdesktop.app/) 8.0.13 [C:\Program Files\dotnet\shared\[Microsoft.WindowsDesktop.App](http://microsoft.windowsdesktop.app/)]
[Microsoft.WindowsDesktop.App](http://microsoft.windowsdesktop.app/) 9.0.2 [C:\Program Files\dotnet\shared\[Microsoft.WindowsDesktop.App](http://microsoft.windowsdesktop.app/)]
Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]
Environment variables:
Not set
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download
Thx for reporting this, I would believe this is not particularly related to sign cli, I'll take a look from the trusted signing's end.
Looks like if we sign some bits, the root certificate is not being publicly trusted:
But this shouldn't be the issue, since private trust is also not publicly trusted, and it is working.
I think the key is that the chain of public trust test is different than the rest of the profile types. I'm still investigating.
Managed to check drilled down into the specific details of the chain build up failure, and it looks like were getting the following validation errors when building the chain:
- System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NotSignatureValid
- System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.OfflineRevocation
- System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.RevocationStatusUnknown
I'm going to track down internally our PKI team to discuss this issue with them.
Looks like the Certificate Profile is the Test and not Public Trust profile. The certificate chain and root for Test is not publicly trusted. That profile type is only for private test signing and any trust would have to be opt-in. Additionally, the end-entity signing certs are Lifetime EKU bound, so even if you are opt-ing in trust and properly timestamping, the signature will only be valid for the life of the end-entity signing certificate to ensure they are only being used for test.
What I find particularly interesting is that private trust (for example, since its not trusted by default) does work on this chain.build, because the root of Public Trust and Private Trust is coming still from Microsoft Enterprise Identity Verification Root Certificate Authority 2020 while Public Trust Test is coming from a completely different root Microsoft Identity Verification TEST ONLY Root Certificate Authority 2020. Hence this causes the experience of failing to sign when it verifies just for public trust test, out of the box.
edit: typos