You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅用原始数据哈希验证PKCS7(CMS)签名的技术实现问询

问题

我尝试用System.Security.Cryptography或BouncyCastle库验证分离式PKCS7(CMS)签名,折腾了好几个小时都没成功。核心问题是:我只能拿到原始数据的哈希值,以及对应的分离式CMS签名,需要验证这个签名的有效性。

当传入原始数据时,下面的代码能正常验证,但我拿不到原始数据,只有哈希值。我推测失败原因是CheckSignature方法会在验证前再次对传入的哈希值做哈希运算,得想办法跳过这一步。有没有不用原始数据就能验证签名的方法?

.NET代码示例(传入原始数据有效,传入哈希值无效):

byte[] originalData = Encoding.UTF8.GetBytes("Test");
byte[] hashedData = new SHA512Managed().ComputeHash(originalData); // hashedData是我随签名收到的数据

var content = new System.Security.Cryptography.Pkcs.ContentInfo(originalData); // 正常工作,但我没有原始数据
var content = new System.Security.Cryptography.Pkcs.ContentInfo(hashedData); // 无法验证通过

var cms = new System.Security.Cryptography.Pkcs.SignedCms(content, true);
cms.Decode(signature);
cms.CheckSignature(true);

我试过在ContentInfo构造函数里指定数据已哈希,但没用。如果.NET实现不了,也可以给BouncyCastle的解决方案,我当前的BouncyCastle代码也只支持原始数据,用哈希值不行:

var certificate = DotNetUtilities.FromX509Certificate(workerCert);
var processable = new CmsProcessableByteArray(hashedData);

var cms = new CmsSignedData(processable, signature);
var signers = cms.GetSignerInfos();
var signersCollection = signers.GetSigners();

foreach (var signer in signersCollection.Cast<SignerInformation>())
{
    if (signer.Verify(certificate.GetPublicKey()))
    {
        Console.WriteLine("Working");
    }
}

.NET 解决方案

要直接用哈希值验证,关键是告诉CMS系统传入的是预计算的哈希值,而不是原始数据。.NET的SignedCms没有直接暴露这个选项,但可以通过手动构造ContentInfo并指定正确的OID来实现:

  1. 确定签名使用的哈希算法OID(比如SHA-512的OID是2.16.840.1.101.3.4.2.3)
  2. 构造包含哈希值的DigestInfo ASN.1结构,将其作为ContentInfo的内容
  3. 用这个ContentInfo初始化SignedCms并执行验证

代码示例(.NET 5+):

using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.Asn1;

byte[] hashedData = ...; // 你收到的哈希值
byte[] signature = ...; // 你收到的CMS签名
Oid hashOid = Oid.FromFriendlyName("SHA512", OidGroup.HashAlgorithm);

// 构造DigestInfo ASN.1结构
var digestInfo = new DigestInfoAsn
{
    DigestAlgorithm = new AlgorithmIdentifierAsn { Algorithm = hashOid.Value },
    Digest = hashedData,
};

byte[] digestInfoBytes;
using (var writer = new AsnWriter(AsnEncodingRules.DER))
{
    digestInfo.Encode(writer);
    digestInfoBytes = writer.Encode();
}

// 创建ContentInfo,指定类型为digestInfo(OID: 1.2.840.113549.1.9.4)
var contentInfo = new ContentInfo(new Oid("1.2.840.113549.1.9.4"), digestInfoBytes);

var cms = new SignedCms(contentInfo, true);
cms.Decode(signature);
cms.CheckSignature(true);

如果使用.NET Framework或低于5的版本,DigestInfoAsn不可用,需要手动构造ASN.1字节流,或者借助BouncyCastle辅助解析。

BouncyCastle 解决方案

BouncyCastle提供了更灵活的验证方式,可以直接跳过内容哈希计算,用给定的哈希值验证签名:

核心思路是从CMS签名中提取签名者的算法信息和签名值,直接用公钥验证签名值与哈希值的匹配性。

代码示例:

using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

byte[] hashedData = ...; // 你收到的哈希值
byte[] signature = ...; // 你收到的CMS签名
X509Certificate certificate = DotNetUtilities.FromX509Certificate(workerCert);

// 解析CMS签名,无需传入原始数据
var cms = new CmsSignedData(signature);
var signerInfos = cms.GetSignerInfos();

foreach (SignerInformation signer in signerInfos.GetSigners())
{
    // 获取签名算法标识符(如SHA512withRSA)
    string signatureAlgId = signer.SignatureAlgorithm.Algorithm.Id;
    ISigner verifier = SignerUtilities.GetSigner(signatureAlgId);
    
    // 初始化验证器,传入公钥
    verifier.Init(false, certificate.GetPublicKey());
    // 传入预计算的哈希值
    verifier.BlockUpdate(hashedData, 0, hashedData.Length);
    
    // 验证签名值是否匹配
    if (verifier.VerifySignature(signer.GetSignature()))
    {
        Console.WriteLine("签名验证通过");
    }
}

这个方法完全绕开了BouncyCastle内置的内容哈希步骤,直接使用你提供的哈希值完成验证,完美适配没有原始数据的场景。


内容的提问来源于stack exchange,提问作者NoConnection

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:45:53