ole*_*ksa 9 c# cryptography digital-signature
我有存储在网络上的X509证书.我可以从远程Windows证书库中读取链.我需要签署一些数据并在签名中包含链,以便以后验证它.
问题是我找不到将证书链放到CsmSigner的方法.我已经读过它从构造函数参数获取证书并尝试使用X509Chain.Build构建一个链.它忽略了证书列表值并且(显然)失败,因为在本地Windows证书库中找不到证书.
请在下面找到我的测试代码(仅当证书本地保存到Windows证书库时才有效)
protected byte[] SignWithSystem(byte[] data, X509Certificate2 cert, X509Certificate[] chain)
{
ContentInfo contentInfo = new ContentInfo(data);
SignedCms signedCms = new SignedCms(contentInfo, true);
CmsSigner cmsSigner = new CmsSigner(cert);
cmsSigner.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1"); //sha256
cmsSigner.IncludeOption = X509IncludeOption.WholeChain;
if (chain != null)
{
//adding cert chain to signer
cmsSigner.Certificates.AddRange(chain);
signedCms.Certificates.AddRange(chain);
}
signedCms.ComputeSignature(cmsSigner); //fails here with System.Security.Cryptography.CryptographicException : A certificate chain could not be built to a trusted root authority.
byte[] signedPkcs = signedCms.Encode();
return signedPkcs;
}
Run Code Online (Sandbox Code Playgroud)
有没有办法让它无需上传证书到本地商店?我应该使用任何替代签名者吗?
我可以尝试将证书上传到商店,但问题是这样
我必须添加和删除证书(必须授予权限)
有几个进程应用签名,因此必须添加跨进程同步.
这不是我想做的.
您可以使用适用于 .NET的BouncyCastle加密库,其中包含自己的 X509 证书和 CMS 签名机制。网络上的许多示例和文档都是针对 Java 的,因为 BouncyCastle 首先是一个 Java 库。我已经使用这个 Stackoverflow 问题的答案作为证书和密钥加载的起点,并添加了 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以使用根不在受信任证书存储中的证书重现您的问题,并确认将证书链添加到cmsSigner/signedCms Certificates集合并不能避免A certificate chain could not be built to a trusted root authority错误。
您可以通过设置成功签名 cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
但是,如果您这样做,您将不会在签名中获得链的其余部分。这可能不是你想要的。
X509Certificate顺便说一句,在您的示例中,您使用的是链中的证书数组,但将它们传递给一个X509Certificate2Collection(注意其中的“2”)。X509Certificate2派生自X509Certificate,但如果它实际上不是X509Certificate2您放入其中一个集合中的,那么如果某些内容在集合上迭代,您将收到强制转换错误(不幸的是,添加错误类型的证书时您不会收到错误消息,因为X509Certificate2Collection也派生自X509CertificateCollection并继承其 add 方法)。