使用 BouncyCastle for .NET 进行 CMS 签名示例
你可以使用充气城堡 http://www.bouncycastle.org/csharp/.NET 的加密库,其中包含自己的 X509 证书和 CMS 签名机制。网络上的许多示例和文档都是针对 Java 的,因为 BouncyCastle 首先是一个 Java 库。我用过Stackoverflow 问题的答案 https://stackoverflow.com/questions/3940998/get-start-with-bouncycastle-crypto-dll-c-sharp作为证书和密钥加载的起点,并添加了 CMS 签名。您可能需要调整参数才能为您的用例产生所需的结果。
我使签名函数看起来与您的大致相似,但请注意私钥现在是一个单独的参数。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.X509.Store;
class Program
{
protected static byte[] SignWithSystem(byte[] data, AsymmetricKeyParameter key, X509Certificate cert, X509Certificate[] chain)
{
var generator = new CmsSignedDataGenerator();
// Add signing key
generator.AddSigner(
key,
cert,
"2.16.840.1.101.3.4.2.1"); // SHA256 digest ID
var storeCerts = new List<X509Certificate>();
storeCerts.Add(cert); // NOTE: Adding end certificate too
storeCerts.AddRange(chain); // I'm assuming the chain collection doesn't contain the end certificate already
// Construct a store from the collection of certificates and add to generator
var storeParams = new X509CollectionStoreParameters(storeCerts);
var certStore = X509StoreFactory.Create("CERTIFICATE/COLLECTION", storeParams);
generator.AddCertificates(certStore);
// Generate the signature
var signedData = generator.Generate(
new CmsProcessableByteArray(data),
false); // encapsulate = false for detached signature
return signedData.GetEncoded();
}
static void Main(string[] args)
{
try
{
// Load end certificate and signing key
AsymmetricKeyParameter key;
var signerCert = ReadCertFromFile(@"C:\Temp\David.p12", "pin", out key);
// Read CA cert
var caCert = ReadCertFromFile(@"C:\Temp\CA.cer");
var certChain = new X509Certificate[] { caCert };
var result = SignWithSystem(
Guid.NewGuid().ToByteArray(), // Any old data for sake of example
key,
signerCert,
certChain);
File.WriteAllBytes(@"C:\Temp\Signature.data", result);
}
catch (Exception ex)
{
Console.WriteLine("Failed : " + ex.ToString());
Console.ReadKey();
}
}
public static X509Certificate ReadCertFromFile(string strCertificatePath)
{
// Create file stream object to read certificate
using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read))
{
var parser = new X509CertificateParser();
return parser.ReadCertificate(keyStream);
}
}
// This reads a certificate from a file.
// Thanks to: http://blog.softwarecodehelp.com/2009/06/23/CodeForRetrievePublicKeyFromCertificateAndEncryptUsingCertificatePublicKeyForBothJavaC.aspx
public static X509Certificate ReadCertFromFile(string strCertificatePath, string strCertificatePassword, out AsymmetricKeyParameter key)
{
key = null;
// Create file stream object to read certificate
using (var keyStream = new FileStream(strCertificatePath, FileMode.Open, FileAccess.Read))
{
// Read certificate using BouncyCastle component
var inputKeyStore = new Pkcs12Store();
inputKeyStore.Load(keyStream, strCertificatePassword.ToCharArray());
var keyAlias = inputKeyStore.Aliases.Cast<string>().FirstOrDefault(n => inputKeyStore.IsKeyEntry(n));
// Read Key from Aliases
if (keyAlias == null)
throw new NotImplementedException("Alias");
key = inputKeyStore.GetKey(keyAlias).Key;
//Read certificate into 509 format
return (X509Certificate)inputKeyStore.GetCertificate(keyAlias).Certificate;
}
}
}
.NET CMS(快速修复,签名中省略了链的其余部分)
我可以使用根不在受信任证书存储中的证书重现您的问题,并确认将证书链添加到cmsSigner
/signedCms
Certificates
收藏并不能避免A certificate chain could not be built to a trusted root authority
error.
通过设置即可签名成功cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
但是,如果您这样做,您将无法获得签名中链的其余部分。这可能不是你想要的。
顺便说一句,在您的示例中您正在使用X509Certificate
对于链中的证书数组,但将它们传递给X509Certificate2Collection
(注意其中的“2”)。X509Certificate2
源自于X509Certificate
,但如果它实际上不是X509Certificate2
如果您将其放入这些集合之一中,则如果对集合进行迭代,您将收到强制转换错误(不幸的是,在添加错误类型的证书时不会收到错误,因为X509Certificate2Collection
也源自X509CertificateCollection
并继承其add方法)。