我不想将 C++/CLI 添加到我的项目中,因此想出了一个使用 System.Security.Pkcs.SignedCms 的纯 C# 解决方案。与 Searcher 的答案类似,这提供了 ISignatureFactory 的实现,可用于在 BouncyCastle 中签署证书。
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
public class WindowsSignatureFactory : ISignatureFactory
{
public AlgorithmIdentifier Algorithm { get; }
public X509Certificate2 Cert { get; }
public object AlgorithmDetails => Algorithm;
public WindowsSignatureFactory(AlgorithmIdentifier algorithm,
X509Certificate2 cert)
{
Algorithm = algorithm;
Cert = cert;
}
public IStreamCalculator CreateCalculator()
{
return new WindowsStreamCalculator(Algorithm, Cert);
}
private class WindowsStreamCalculator : IStreamCalculator
{
public AlgorithmIdentifier Algorithm { get; }
public X509Certificate2 Cert { get; }
private MemoryStream MemoryStream { get; } = new MemoryStream();
public Stream Stream => MemoryStream;
public WindowsStreamCalculator(AlgorithmIdentifier algorithm,
X509Certificate2 cert)
{
Algorithm = algorithm;
Cert = cert;
}
public object GetResult()
{
var signer = new System.Security.Cryptography.Pkcs.CmsSigner(Cert);
// This maps e.g. "rsa with sha256" (OID 1.2.840.113549.1.1.11) to
// "sha256" (OID 2.16.840.1.101.3.4.1)
var hashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(Algorithm);
var hashOid = hashAlgorithm.Algorithm.Oid;
signer.DigestAlgorithm = new System.Security.Cryptography.Oid(hashOid);
var dataToSign = MemoryStream.ToArray();
var info = new System.Security.Cryptography.Pkcs.ContentInfo(dataToSign);
var cms = new System.Security.Cryptography.Pkcs.SignedCms(info, true);
cms.ComputeSignature(signer, true);
// This is the tricky part: usually you would call cms.Export() to get a signed
// message, but we only want the signature
var cmsSigner = cms.SignerInfos
.Cast<System.Security.Cryptography.Pkcs.SignerInfo>().ToArray().Single();
var signatureData = cmsSigner.GetSignature();
return new SimpleBlockResult(signatureData);
}
}
}
它可以用作 Asn1SignatureFactory 的替代品,如下所示:
public static Org.BouncyCastle.X509.X509Certificate SignCertificate(
X509V3CertificateGenerator generator,
X509Certificate2 nonExportableSigningCertificate)
{
var signer = new WindowsSignatureFactory(
new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha256),
nonExportableSigningCertificate);
var signed = generator.Generate(signer);
// Make sure it worked
var signingCertBouncy = DotNetUtilities.FromX509Certificate(nonExportableSigningCertificate);
signed.Verify(signingCertBouncy.GetPublicKey());
return signed;
}