使用已安装的X.509证书对xml文档进行签名

时间:2017-11-03 17:20:42

标签: xml x509certificate signing x509certificate2 certificate-store

我在使用已安装的证书签署xml docuemnt时遇到问题。我尝试使用证书文件(.pfx)安装在LocalMachine,CurrentUser和X709Certificate2的Initialize实例中的证书。每个人都有自己的问题;我的偏好是使用LocalMachine商店中安装的证书。下面我概述了三种方法以及每种方法的关注点:

StoreLocation LocalMachine - 首选方法

var certStore = new X509Store(StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbPrint, true);
var myCert = certCollection[0];

// I can get the correct certificate but the following line throws "Invalid provider type specified." error
var SigningKey = myCert.PrivateKey;

StoreLocation CurrentUser

var certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbPrint, true);
var myCert = certCollection[0];

// I can get the correct certificate but the following line throws "Keyset does not exist" error
var SigningKey = myCert.PrivateKey;

如果我将权限更改为以下文件夹%ALLUSERSPROFILE%\ Application Data \ Microsoft \ Crypto \ RSA \ MachineKeys,我只能获取PrivateKey。这看起来不是实施签名的正确方法。

使用证书文件

var certificateFile = @"C:\CertificateFolder\AuthorizedCertificate.pfx";
var myCert = new X509Certificate2(certificateFile, password, X509KeyStorageFlags.UserKeySet);

此方法有效但我必须提供证书文件和密码,这是不可取的。

如何让第一种方法(LocalMachine)起作用?建议/最佳实践方法是什么?

作为参考,以下代码用于签署xml文档

private void SignXml(XmlDocument xmlDoc, X509Certificate2 cert)
{
    // Create a SignedXml object.
    SignedXml signedXml = new SignedXml(xmlDoc);

    // Add the key to the SignedXml document.
    signedXml.SigningKey = cert.PrivateKey;

    // Create a reference to be signed.
    Reference reference = new Reference();
    reference.Uri = "";

    // Add an enveloped transformation to the reference.
    var env = new XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(env);

    // Include the public key of the certificate in the assertion.
    signedXml.KeyInfo = new KeyInfo();
    signedXml.KeyInfo.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.WholeChain));

    // Add the reference to the SignedXml object.
    signedXml.AddReference(reference);

    // Compute the signature.
    signedXml.ComputeSignature();

    // Get the XML representation of the signature and save
    // it to an XmlElement object.
    XmlElement xmlDigitalSignature = signedXml.GetXml();

    // Append the element to the XML document.
    xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
}

1 个答案:

答案 0 :(得分:0)

LocalMachine商店版本

替换

var SigningKey = myCert.PrivateKey;

var SigningKey = myCert.GetRSAPrivateKey();

(s / RSA / DSA /视情况而定)

PrivateKey属性只能返回可转换为RSACryptoServiceProvider或DSACryptoServiceProvider的键。 “指定的提供程序类型无效”表示您的私钥存储在CNG中,而不是CAPI。

这只适用于安装了.NET 4.6.2或更高版本的情况,因为当SignedXml及其帮助程序类中的某些限制(关于非RSACryptoServiceProvider RSA)被修复时。

(替代方案:升级到Windows 10,操作系统将CNI添加到CNG桥来解决此问题)

CurrentUser商店版本

此版本失败,因为从PFX导入证书时,您使用MachineKeySet导入证书(或者您未指定UserKeySet,并且之前已从计算机密钥存储区导出)。用户存储中的证书副本表明它的私钥存在于机器商店中。并且,由于某种原因,您无法访问它。 (“由于某种原因”,因为通常会建议你不能添加它......)

PFX版

这是有效的,因为PFX表示密钥应存储在CAPI CSP中(PFX携带大量元数据),允许证书PrivateKey属性起作用。