我正在尝试使用SHA256对XML文档进行数字签名。
我正在尝试使用Security.Cryptography.dll。
这是我的代码 -
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription),"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
X509Certificate2 cert = new X509Certificate2(@"location of pks file", "password");
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(@"input.xml");
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.PrivateKey;
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
//
// Add a signing reference, the uri is empty and so the whole document
// is signed.
Reference reference = new Reference();
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.Uri = "";
signedXml.AddReference(reference);
//
// Add the certificate as key info, because of this the certificate
// with the public key will be added in the signature part.
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(cert));
signedXml.KeyInfo = keyInfo;
// Generate the signature.
signedXml.ComputeSignature();
但我得到“指定了无效的算法”。 signedXml.ComputeSignature();
处的错误。谁能告诉我我做错了什么?
答案 0 :(得分:30)
X509Certificate2
将私有密钥从pfx文件加载到 Microsoft增强加密提供程序v1.0 (提供程序类型1
又名PROV_RSA_FULL
)中,而不是&# 39; t支持SHA-256。
基于CNG的加密提供程序(在Vista和Server 2008中引入)支持的算法比基于CryptoAPI的提供程序更多,但.NET代码似乎仍在使用基于CryptoAPI的类,如RSACryptoServiceProvider
而不是RSACng
所以我们必须解决这些限制。
但是,另一个CryptoAPI提供程序, Microsoft增强型RSA和AES加密提供程序(提供程序类型24
a.k.a。PROV_RSA_AES
)确实支持SHA-256。因此,如果我们将私钥发送到此提供商,我们可以使用它进行签名。
首先,您必须调整X509Certificate2
构造函数,以便通过添加X509Certificate2
标记将密钥从X509KeyStorageFlags.Exportable
放入的提供程序中导出:
X509Certificate2 cert = new X509Certificate2(
@"location of pks file", "password",
X509KeyStorageFlags.Exportable);
导出私钥:
var exportedKeyMaterial = cert.PrivateKey.ToXmlString(
/* includePrivateParameters = */ true);
然后为支持SHA-256的提供程序创建一个新的RSACryptoServiceProvider
实例:
var key = new RSACryptoServiceProvider(
new CspParameters(24 /* PROV_RSA_AES */));
key.PersistKeyInCsp = false;
并将私钥导入其中:
key.FromXmlString(exportedKeyMaterial);
当您创建了SignedXml
个实例后,请告诉它使用key
而不是cert.PrivateKey
:
signedXml.SigningKey = key;
现在它将起作用。
以下是MSDN上的list of provider types and their codes。
以下是您的示例的完整调整代码:
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
X509Certificate2 cert = new X509Certificate2(@"location of pks file", "password", X509KeyStorageFlags.Exportable);
// Export private key from cert.PrivateKey and import into a PROV_RSA_AES provider:
var exportedKeyMaterial = cert.PrivateKey.ToXmlString( /* includePrivateParameters = */ true);
var key = new RSACryptoServiceProvider(new CspParameters(24 /* PROV_RSA_AES */));
key.PersistKeyInCsp = false;
key.FromXmlString(exportedKeyMaterial);
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(@"input.xml");
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = key;
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
//
// Add a signing reference, the uri is empty and so the whole document
// is signed.
Reference reference = new Reference();
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.Uri = "";
signedXml.AddReference(reference);
//
// Add the certificate as key info, because of this the certificate
// with the public key will be added in the signature part.
KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(cert));
signedXml.KeyInfo = keyInfo;
// Generate the signature.
signedXml.ComputeSignature();
答案 1 :(得分:15)
导出和重新导入已经been given as an answer,但您还应该了解其他几个选项。
GetRSAPrivateKey(扩展名)方法返回"最佳可用类型"的RSA实例。对于密钥和平台(而不是PrivateKey属性,#34;每个人都知道"返回RSACryptoServiceProvider)。
在99.99(%)%的所有RSA私钥中,此方法返回的对象能够生成SHA-2签名。
虽然在.NET 4.6(.0)中添加了该方法,但在这种情况下存在4.6.2的要求,因为从GetRSAPrivateKey返回的RSA实例不能与SignedXml一起使用。从那以后been fixed(162556)。
我个人不喜欢这种方法,因为它使用(现在遗留的)PrivateKey属性和RSACryptoServiceProvider类。但是,它具有处理所有.NET Framework版本的优势(尽管在非Windows系统上不是.NET Core,因为RSACryptoServiceProvider仅适用于Windows)。
private static RSACryptoServiceProvider UpgradeCsp(RSACryptoServiceProvider currentKey)
{
const int PROV_RSA_AES = 24;
CspKeyContainerInfo info = currentKey.CspKeyContainerInfo;
// WARNING: 3rd party providers and smart card providers may not handle this upgrade.
// You may wish to test that the info.ProviderName value is a known-convertible value.
CspParameters cspParameters = new CspParameters(PROV_RSA_AES)
{
KeyContainerName = info.KeyContainerName,
KeyNumber = (int)info.KeyNumber,
Flags = CspProviderFlags.UseExistingKey,
};
if (info.MachineKeyStore)
{
cspParameters.Flags |= CspProviderFlags.UseMachineKeyStore;
}
if (info.ProviderType == PROV_RSA_AES)
{
// Already a PROV_RSA_AES, copy the ProviderName in case it's 3rd party
cspParameters.ProviderName = info.ProviderName;
}
return new RSACryptoServiceProvider(cspParameters);
}
如果您已将cert.PrivateKey转换为RSACryptoServiceProvider,则可以通过UpgradeCsp发送它。由于这是打开现有密钥,因此没有额外的材料写入磁盘,它使用与现有密钥相同的权限,并且不需要您进行导出。
但是(请注意!)不要设置PersistKeyInCsp = false,因为这将在克隆关闭时删除原始密钥。
答案 2 :(得分:2)
如果升级到.Net 4.7.1或更高版本后遇到此问题:
.Net 4.7及更低版本:
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.PrivateKey;
.net 4.7.1及更高版本:
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = cert.GetRSAPrivateKey();
弗拉基米尔·科赞西奇
的积分答案 3 :(得分:0)
在 dotnet core 中,我有这个:
var xml = new SignedXml(request) {SigningKey = privateKey};
xml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
xml.SignedInfo.SignatureMethod = SignedXml.XmlDsigSHA256Url;
xml.KeyInfo = keyInfo;
xml.AddReference(reference);
xml.ComputeSignature();
没有用。相反,我使用了这个
var xml = new SignedXml(request) {SigningKey = privateKey};
xml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
xml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA256Url;
xml.KeyInfo = keyInfo;
xml.AddReference(reference);
xml.ComputeSignature();
更改签名方法 => xml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA256Url