CMS登录与证书链不.NET本地受信任的证书存储 [英] CMS signing in .NET with certificate chain not in local trusted certificate store
问题描述
我具有被存储在网络上X509证书。我可以读取远程Windows证书存储链。我需要签署一些数据,包括链的签名,以使人们有可能在以后对其进行验证。
I have X509 certificates that are stored on the network. I can read the chain from remote windows certificate store. I need to sign some data and include chain to the signature to make it possible to validate it later.
的问题是,我无法找到一个办法把证书链到CsmSigner。我已阅读,它从构造函数的参数证书,并试图建立与X509Chain.Build链。它忽略证书列表值和失败(显然),因为没有证书可以在本地Windows证书存储中找到。
The problem is that I can't find a way to put certificate chain to the CsmSigner. I have read that it takes certificate from constructor parameter and tries to build a chain with X509Chain.Build. It ignores Certificates list values and fails (obviously) because no certificate can be found in the local Windows cert store.
请在下面找到我的测试代码(即仅当证书在本地保存到Windows证书存储)
Please find below my test code (that works only if certificates were saved locally to the windows cert store)
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;
}
有没有什么办法让它不上传证书到当地商店工作?我应该使用任何替代签名?
Is there any way to make it work without uploading certificates to the local store? Should I use any alternative signer?
我可以尝试上传证书到店,但问题是,
I can try to upload certificates to the store but the problems are that
-
我有添加和删除证书(权限都被授予)
I have to add and remove certificates (permissions have to be granted)
有几种工艺适用签名所以跨进程同步必须添加。
There are several processes that applies signature so cross-process synchronization have to be added.
这是不是我想要做的。
推荐答案
您可以使用 BouncyCastle的加密的.NET库,其中包含了自己的X509证书和CMS签名机械。很多在网络上的实例和文档的Java,作为BouncyCastle的是一个Java库第一。我用回答这个问题#1 作为起点证书和密钥加载,并添加CMS签名。您可能需要调整参数产生你想为你的用例的结果。
Example CMS Signing with BouncyCastle for .NET
You could use the BouncyCastle crypto library for .NET, which contains its own X509 certificate and CMS signing machinery. A lot of the examples and documentation on the web are for Java, as BouncyCastle was a Java library first. I've used the answer to this Stackoverflow question as a starting point for the certificate and key loading, and added the CMS signing. You may have to tweak parameters to produce the results you want for your use case.
我所做的签约功能看起来大约像你这样的,但要注意的私钥现在一个单独的参数
I've made the signing function look approximately like yours, but note the private key is a separate parameter now.
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(的Quick与链的其余部分自签字省略)
解决我可以用其根不在受信任的证书存储中的证书重现你的问题,并确认将证书链在 cmsSigner
/ signedCms
证书
收集不避 A证书链无法建立到受信任的根证书颁发机构
错误。
.NET CMS (Quick-fix with rest of chain omitted from signature)
I can reproduce your problem with a certificate whose root is not in the trusted certificate store, and confirm that adding the certificate chain to the cmsSigner
/signedCms
Certificates
collection does not avoid the A certificate chain could not be built to a trusted root authority
error.
您可以通过设置<$ C成功登录$ C> cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly;
不过,如果你这样做,你不会得到链在休息签名。这可能不是你想要的。
However, if you do this, you will not get the rest of the chain in the signature. This probably isn't what you want.
顺便说一句,在你的榜样,你正在使用 X509证书
的阵列中的链的证书,但将它们传递给一个 X509Certificate2Collection
(注意在那里2)。 X509Certificate2
从 X509证书
派生的,但如果它并不是一个真正的 X509Certificate2
你把这些收藏品之一,你会得到一个错误投,如果事情遍历集合(可惜加错误类型的证书,当你没有得到一个错误,因为 X509Certificate2Collection
也由 X509CertificateCollection
并继承了它添加方法)。
As an aside, in your example you are using X509Certificate
for the array of certificates in the chain, but passing them to an X509Certificate2Collection
(note the "2" in there). X509Certificate2
derives from X509Certificate
, but if its not actually an X509Certificate2
that you put in one of those collections, you'll get a cast error if something iterates over the collection (you don't get an error when adding a certificate of the wrong type unfortunately, because X509Certificate2Collection
also derives from X509CertificateCollection
and inherits its add methods).
这篇关于CMS登录与证书链不.NET本地受信任的证书存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!