runtime: SignedCms considers signature invalid on .NET Core but valid on .NET Framework
While @onovotny and I were experimenting with NuGet package signing on netcoreapp2.1 on Windows, we seem to have run in to a behavioral difference of SignedCms between .NET Core and the .NET Framework. The .NET Framework considers a PKCS#7 correctly signed, while .NET Core does not.
When NuGet timestamps a package, it validates the received timestamp after receiving the timestamp
We noticed that when running on .NET Core 2.1, on Windows, the validation step fails.
I distilled this down to what appears to be a behavioral difference between .NET Core and Desktop .NET Framework from SignedCms.
I produced a small repro here that basically:
- Has a base-64 encoded PKCS#7 object in
tst.txt. This came fromCryptRetrieveTimeStampwhich is the same Win32 API that NuGet is using. - Loads it in to a
SignedCms. - Calls
CheckSignatureon the firstSignerInfo.
# Does not throw an exception
dotnet run --framework net471
and
# Throw an invalid signature exception
dotnet run --framework netcoreapp2.1
Both of these were run on Window 10 x64 1803.
Further more, it appears that SignedCms prevents the PKCS#7 object from round-tripping correctly. The PKCS#7 in tst.txt is valid according to the .NET Framework. If I load the signature into a SignedCms in .NET Core, then export again, the .NET Framework will not consider the re-exported PKCS#7 CMS valid anymore.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 1
- Comments: 27 (24 by maintainers)
To close the loop on a thread here, I got word from DigiCert (@clintwilson) that their TSA is now properly sorting DER sets.
I tried several timestamps I produced a few minutes ago, and validating the timestamp did not use the
compatMode: truepath forValidateSignature.Thanks Clint!
/cc @bartonjs @onovotny
Approved for 2.1.5
Fails on the last CheckSignature. I’ll also add one for countersigning and additional-signing.
I’m sad at the amount of code required to go from “spec compliant” to “real-world compatible”.
Shiproom template
Description
If the creator of a CMS SignedData document does not sort a signer’s signed attributes before computing their signature then .NET Core will report the signature as invalid, but .NET Framework reports it as valid.
Customer Impact
Customers who receive signed documents which were built over unsorted attributes will be told they are invalid on .NET Core, even if they were told they were valid on .NET Framework. Since at least one free cryptographic time-stamping service is known to have this behavior, it can lead to situations where .NET Framework says a Signed NuGet Package is valid, but .NET Core says it is invalid.
Regression?
Behavioral regression from .NET Framework (the .NET Core implementation was new for netcoreapp2.1).
Risk
Low. The solution is just to try processing attributes in the file-specified order if sorted order fails, which matches the Crypt32 behavior.
Loading your test document in and stepping through the process I see
FD1766E9C1772950D946AB845EC105961236914A2BD4273980C7851E24E8FFAA, which matches the value of the messageDigest attribute.57E6A51620AB549890DFEC1494753BE1C2C8A8D6230FF15AD7B722E1A542F59A.A46F7471F64F2B6DB38A9A540AF64525FB448A047C3B17106D75CDBE2ED18D0EI see that the attributes are not listed in DER order (the last two are sorted incorrectly), making me wonder if Windows isn’t actually doing the required-by-DER sort on hash computation/verification. (I’ll recompute the hash with sorting off after some meetings). If that is the problem, then I’ll have to compare it with other implementations to see if it’s “do what the writer says” or “do what the spec says” or “try both ways” in practice.
Partially confirmed, if I disable the sorting required for SET-OF under DER, the SignerInfo digest is
A46F7471F64F2B6DB38A9A540AF64525FB448A047C3B17106D75CDBE2ED18D0E