John Källén
John Källén

Reputation: 7963

SignedCms.CheckSignature() always fails when using a valid certificate

I'm trying to use SignedCms.CheckSignature in .NET Framework 4.7.2 to validate a message I know is valid using a certificate I know is valid. I'm using the following code to do this:

    using System.Security.Cryptography.Pkcs;
    using System.Security.Cryptography.X509Certificates;
    using System.Collections.Generic;

    public class VerifySignature {

        public static void Main(string [] args) {
            byte[] signature = FromHexString(secKey);
            byte[] certBytes = FromHexString(sCert);
            var certificate = new X509Certificate2(certBytes);
            var collection = new X509Certificate2Collection(certificate);
            var verifyCms = new SignedCms();
            verifyCms.Decode(signature);
            verifyCms.CheckSignature(collection, true);
        }

        // Disposable certificate + secKey
        private const string sCert = @"3082045406092A864886F70D010702A082044530820441020101310B300906052B0E03021A0500300B06092A864886F70D010701A08202FF308202FB308202BB020720110818132047300906072A8648CE3804033064310B3009060355040613024445311C301A060355040A131353415020547275737420436F6D6D756E69747931133011060355040B130A5341502057656220415331143012060355040B130B4930313230303033343131310C300A06035504031303494433301E170D3131303831383133323034375A170D3338303130313030303030315A3064310B3009060355040613024445311C301A060355040A131353415020547275737420436F6D6D756E69747931133011060355040B130A5341502057656220415331143012060355040B130B4930313230303033343131310C300A06035504031303494433308201B73082012C06072A8648CE3804013082011F02818100FFF96C9CDD661022E93DF5B27DB6C6C9FF34358366D2C7C3C37838703E4A4876429227017338D65A73617D5640C6764C67B2E5BE771F4937C6AC43E96780A57CA64C47ADE6C6D0336E5CA39D77035EA1F836A45CA1C0255D7AC473C9C9B09E40C07D2AC3B0C72F27273AA9F0B0221CB484A5A3E565D3540531A170E7E9ACFD4302150095C9AE5339A3D29AAA7A3705C884166335740DD302818100F808A1CE9A4C20F2FA4470BD5D9AAA4E69E18E5F5F182272770050B86EFD750011EBF938ADA9F63E6956F533E2B829C28A407A2D5735F41E020E36D1CBA8092000C597BE2A6022AED8BC95C720CE8465EAD415B19F1560964EC0422A9A5C9DADDC373AAF8F90AB6E6248A74F3A51EF4A5E06346FE3270449E7E8B2E88178450E038184000281806A7DE80E4DAAE5CB95DC79D7C3C2B15EDE1973453E09AF4EBDCDFF55ADA9256C0FC4E98EE443D5916D9CB6C54BD7D9612A02693BEF866BE4C4777E159121EE285A6199FF30AF309E675B4E1ADAE95E4A5254CBC37C49C77EC9A3169B5BDA1D7FFB24C27334B7A0E3E6FBEC4257C0C1C3F6CDAE5D3F8748F52607B399BCF0A61C300906072A8648CE380403032F00302C02144AC6E9813C2F7EFEEFED0A9FE60E4816DA964B9F02145327E3FE9FA347864ABCCC0198E519AEEE678C7E3182011D30820119020101306F3064310B3009060355040613024445311C301A060355040A131353415020547275737420436F6D6D756E69747931133011060355040B130A5341502057656220415331143012060355040B130B4930313230303033343131310C300A06035504031303494433020720110818132047300906052B0E03021A0500A05D301806092A864886F70D010903310B06092A864886F70D010701301C06092A864886F70D010905310F170D3230303132393139343630355A302306092A864886F70D01090431160414B858CB282617FB0956D960215C8E84D1CCF909C6300906072A8648CE380403042E302C02143DED3FA580EF178F0190EB66033EF0E2FFD647CC02146857D6E9123802BC9589D3B7D52BB8A11C348081";
        private const string secKey = @"3082015106092A864886F70D010702A08201423082013E020101310B300906052B0E03021A0500300B06092A864886F70D0107013182011D30820119020101306F3064310B3009060355040613024445311C301A060355040A131353415020547275737420436F6D6D756E69747931133011060355040B130A5341502057656220415331143012060355040B130B4930313230303033343131310C300A06035504031303494433020720110818132047300906052B0E03021A0500A05D301806092A864886F70D010903310B06092A864886F70D010701301C06092A864886F70D010905310F170D3230303132393232323635385A302306092A864886F70D010904311604141A1A7B63F70EA93616A10297BA4D27FB9255753B300906072A8648CE380403042E302C0214323B9B50FAAD0823C14A6565B0CA291DBC0A967B0214511F586BFB544E63256CD49DA1FF03FF2984F640";

        private byte[] FromHexString(string hexString) {
            var bytes = new List<byte>();
            int by = 0;
            int hexDigits = 0;
            for (int i = 0; i < hexString.Length; ++i) {
                char c = hexString[i];
                if ('0' <= c && c <= '9') {
                    by = (by << 4) | (c - '0');
                    ++hexDigits;
                } else if ('A' <= c && c <= 'F') {
                    by = (by << 4) | (c - 'A' + 10);
                    ++hexDigits;
                }
                if (hexDigits == 2) {
                    bytes.Add((byte)by);
                    hexDigits = 0;
                }
            }
            return bytes.ToArray();
        }
    }

I have verified that the X509Certificate2 is valid, and the secKey is correctly decoded. And yet, when I call SignedCms.CheckSignature I get an cryptographic exception with the message "The hash value is not correct."

Are my expectations that this code should work incorrect?

Upvotes: 1

Views: 2024

Answers (1)

bartonjs
bartonjs

Reputation: 33238

Your CMS SignedData value from secKey is built with detached content, meaning it's just the signature. The way you're trying to verify it, it is verifying that the signature applies to new byte[0].

  • new byte[0] has a SHA-1 hash of DA39A3EE5E6B4B0D3255BFEF95601890AFD80709
  • The signature applies to content with a SHA-1 hash of 1A1A7B63F70EA93616A10297BA4D27FB9255753B
  • Exception: The hash value is not correct.

You need to find the content, and change your document construction to

ContentInfo detachedData = new ContentInfo(data);
SignedCms verifyCms = new SignedCms(detachedData, detached: true);
// rest of code goes here.

Once the internal digest can be verified, the signature will successfully verify given the public key (based on some data manipulation within a debugger).

Upvotes: 3

Related Questions