使用已安装的 X.509 证书签署 xml 文档
Signing xml document with a installed X.509 certificate
我在使用安装的证书签署 xml 文档时遇到问题。我尝试使用证书文件 (.pfx) 在 X509Certificate2 的 LocalMachine、CurrentUser 和 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 当前用户
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,我只能获取私钥。这看起来不是实现签名的正确方法。
使用证书文件
var certificateFile = @"C:\CertificateFolder\AuthorizedCertificate.pfx";
var myCert = new X509Certificate2(certificateFile, password, X509KeyStorageFlags.UserKeySet);
此方法有效,但我必须提供证书文件和密码,这是不可取的。
如何让第一个方法 (LocalMachine) 起作用? recommended/best 练习方法是什么?
作为参考,以下代码用于签署 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));
}
本地机器商店版本
替换
var SigningKey = myCert.PrivateKey;
和
var SigningKey = myCert.GetRSAPrivateKey();
(s/RSA/DSA/视情况而定)
PrivateKey 属性 只能 return 可转换为 RSACryptoServiceProvider 或 DSACryptoServiceProvider 的密钥。 “指定的提供商类型无效”表示您的私钥存储在 CNG 中,而不是 CAPI。
这仅在您安装了 .NET 4.6.2 或更高版本时才有效,因为那时 SignedXml 及其助手 类 中的某些限制(关于非 RSACryptoServiceProvider RSA)已得到修复。
(备选方案:升级到 Windows 10,其中 OS 添加了一个 CAPI 到 CNG 桥来解决这个问题)
当前用户存储版本
此版本失败,因为当您从 PFX 导入证书时,您使用 MachineKeySet 导入了它(或者您没有指定 UserKeySet,并且它之前是从计算机密钥存储中导出的)。用户存储中的证书副本表明它的私钥存在于机器存储中。而且,出于某种原因,您无权访问它。 (“出于某种原因”,因为通常这表明您无法添加它...)
PFX 版本
这是可行的,因为 PFX 表示密钥应存储在 CAPI CSP 中(PFX 携带大量元数据),从而允许证书私钥 属性 发挥作用。
我在使用安装的证书签署 xml 文档时遇到问题。我尝试使用证书文件 (.pfx) 在 X509Certificate2 的 LocalMachine、CurrentUser 和 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 当前用户
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,我只能获取私钥。这看起来不是实现签名的正确方法。
使用证书文件
var certificateFile = @"C:\CertificateFolder\AuthorizedCertificate.pfx";
var myCert = new X509Certificate2(certificateFile, password, X509KeyStorageFlags.UserKeySet);
此方法有效,但我必须提供证书文件和密码,这是不可取的。
如何让第一个方法 (LocalMachine) 起作用? recommended/best 练习方法是什么?
作为参考,以下代码用于签署 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));
}
本地机器商店版本
替换
var SigningKey = myCert.PrivateKey;
和
var SigningKey = myCert.GetRSAPrivateKey();
(s/RSA/DSA/视情况而定)
PrivateKey 属性 只能 return 可转换为 RSACryptoServiceProvider 或 DSACryptoServiceProvider 的密钥。 “指定的提供商类型无效”表示您的私钥存储在 CNG 中,而不是 CAPI。
这仅在您安装了 .NET 4.6.2 或更高版本时才有效,因为那时 SignedXml 及其助手 类 中的某些限制(关于非 RSACryptoServiceProvider RSA)已得到修复。
(备选方案:升级到 Windows 10,其中 OS 添加了一个 CAPI 到 CNG 桥来解决这个问题)
当前用户存储版本
此版本失败,因为当您从 PFX 导入证书时,您使用 MachineKeySet 导入了它(或者您没有指定 UserKeySet,并且它之前是从计算机密钥存储中导出的)。用户存储中的证书副本表明它的私钥存在于机器存储中。而且,出于某种原因,您无权访问它。 (“出于某种原因”,因为通常这表明您无法添加它...)
PFX 版本
这是可行的,因为 PFX 表示密钥应存储在 CAPI CSP 中(PFX 携带大量元数据),从而允许证书私钥 属性 发挥作用。