我必须使用证书和私钥对C#中的某些数据进行数字签名。我正在使用的证书具有自定义根CA以及自定义中间CA.
我可以使用这样的代码进行签名而没有任何问题,如果将根CA和中间CA安装到Windows证书库中:
var content = new ContentInfo(manifest);
var cms = new SignedCms(content, true);
var signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, myCertificate);
signer.SignedAttributes.Add(new Pkcs9SigningTime(DateTime.Now));
cms.ComputeSignature(signer);
return cms.Encode();
不幸的是,无论如何,我无法将根CA和中间CA安装到服务器的证书存储区(我正在使用Azure Web Apps)。我正在尝试使用根和中间CA证书进行签名,如果它们存储在磁盘上而不是。在调用cms.Encode()
:
// add CAs from disk
var intermediateCACertificate = new X509Certificate2(@"pathToIntermediateCertificate.cer");
signer.Certificates.Add(intermediateCACertificate);
var rootCACertificate = new X509Certificate2(@"pathToRootCertificate.cer");
signer.Certificates.Add(rootCACertificate);
但是当我执行此操作时,我会抛出一个CryptographicException "A certificate chain could not be built to a trusted root authority."
。
是否可以使用非标准CA进行数字签名,而无需将其安装到Windows证书存储中?
答案 0 :(得分:2)
有可能。
基本上,从SignedCms
类返回的BLOB除了签名本身之外还能够包含任意数量的任意证书。通常这样做是为了至少包括消息签名的证书以及可能的任何其他中间证书,以便接收实体可以验证签名,直到它信任的根证书。
当您调用上面的signer.Certificates.Add()
调用时,您将添加要在输出签名BLOB中编码的证书。但是,向此集合添加证书并不意味着对它们有任何“信任”。
在您的情况下,问题在于,默认情况下,.NET SignedCms
类会尝试通过自动包含典型方案中包含的证书来帮助您。默认情况下,它包括除根之外的链中的每个证书。它通过尝试使用在服务器的证书存储中找到的证书构建链来实现这一点,正如您所注意到的那样,当未安装根/中间CA的证书时,该证书会失败。
证书链构建由CmsSigner.IncludeOption
属性控制。默认值为X509IncludeOption.ExcludeRoot
。此值和X509IncludeOption.WholeChain
都将在您的系统上失败,因为该类将无法根据证书库中的内容构建链。
可能你想做的事情如下:
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.Certificates.Add(intermediateCACertificate);
signer.Certificates.Add(rootCACertificate);
这将包括您要签名的证书(指定EndCertOnly
时包含的证书)以及明确添加的中间证书和根证书。 SignedCms
不会尝试构建证书链,因为它只需要包含已经提供的证书,因此不会抛出任何异常。
顺便说一下,你可以得到与上面相同的结果:
signer.IncludeOption = X509IncludeOption.None;
signer.Certificates.Add(myCertificate);
signer.Certificates.Add(intermediateCACertificate);
signer.Certificates.Add(rootCACertificate);
在这种情况下,签名证书不会自动包含在内,但下一行会明确地将其添加到集合中。
根据您的收件人信任的CA,您可能不需要像上面那样包含链中的所有证书,并且您可能只能包含签名证书。但是,我建议手动包括至少但不包括root,因为这是.NET的默认行为。实际上,包含整个链条也没有真正的危害,但是对于所有意图和目的,接收方应该已经信任根CA,否则PKI的整个概念就会崩溃。