SignedCms.CheckSignature() 使用有效证书时总是失败

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

我正在尝试使用 .NET Framework 4.7.2 中的 SignedCms.CheckSignature 来验证我知道有效的消息,并使用我知道有效的证书。我正在使用以下代码来执行此操作:

    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();
        }
    }

我已验证 X509Certificate2 有效,secKey 已正确解码。然而,当我调用 SignedCms.CheckSignature 时,我收到一个密码异常,消息为 "The hash value is not correct."

我对这段代码应该工作的期望是否不正确?

来自 secKey 的 CMS SignedData 值是使用分离的内容构建的,这意味着它只是签名。您尝试验证它的方式是验证签名是否适用于 new byte[0].

  • new byte[0] 的 SHA-1 散列为 DA39A3EE5E6B4B0D3255BFEF95601890AFD80709
  • 签名适用于 SHA-1 散列为 1A1A7B63F70EA93616A10297BA4D27FB9255753B 的内容
  • 异常:散列值不正确。

您需要找到内容,并将您的文档结构更改为

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

一旦可以验证内部摘要,签名将在给定 public 密钥的情况下成功验证(基于调试器中的一些数据操作)。