C# 带SHA256的SignedXml计算签名
我正在尝试使用SHA256对XML文档进行数字签名 我正试着用这个 这是我的密码-C# 带SHA256的SignedXml计算签名,c#,xml,encryption,digital-signature,C#,Xml,Encryption,Digital Signature,我正在尝试使用SHA256对XML文档进行数字签名 我正试着用这个 这是我的密码- CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription),"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); X509Certificate2 cert = new X509Certificate2(@"location of pks file", "password"); X
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()处出错代码>。有人能告诉我我做错了什么吗?X509Certificate2
将私钥从pfx文件加载到不支持SHA-256的Microsoft增强加密提供程序v1.0(提供程序类型1
a.k.a.PROV\u RSA\u FULL
)中
基于CNG的加密提供程序(在Vista和Server 2008中引入)比基于CryptoAPI的提供程序支持更多的算法,但.NET代码似乎仍然使用基于CryptoAPI的类,如RSACng
,而不是RSACng
,因此我们必须克服这些限制
但是,另一个CryptoAPI提供程序Microsoft增强的RSA和AES加密提供程序(提供程序类型24
a.k.a.PROV\u RSA\u AES
)确实支持SHA-256。因此,如果我们将私钥放入该提供者,我们可以使用它进行签名
首先,您必须调整X509Certificate2
构造函数,以便通过添加X509KeyStrageFlags.Exportable
标志,从X509Certificate2
放入的提供程序中导出密钥:
X509Certificate2 cert = new X509Certificate2(
@"location of pks file", "password",
X509KeyStorageFlags.Exportable);
并导出私钥:
var exportedKeyMaterial = cert.PrivateKey.ToXmlString(
/* includePrivateParameters = */ true);
然后为支持SHA-256的提供程序创建一个新的RSACryptServiceProvider
实例:
var key = new RSACryptoServiceProvider(
new CspParameters(24 /* PROV_RSA_AES */));
key.PersistKeyInCsp = false;
并将私钥导入其中:
key.FromXmlString(exportedKeyMaterial);
创建了SignedXml
实例后,告诉它使用key
而不是cert.PrivateKey
:
signedXml.SigningKey = key;
现在它将起作用
以下是MSDN上的详细信息
下面是您的示例的完整调整代码:
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.使用GetRSAPrivateKey和.NET 4.6.2(当前处于预览中)
(extension)方法返回密钥和平台的“最佳可用类型”的RSA实例(与PrivateKey属性相反,“每个人都知道”返回RSACryptServiceProvider)
在99.99(etc)%的所有RSA私钥中,此方法返回的对象能够生成SHA-2签名
虽然在.NET 4.6(.0)中添加了该方法,但在本例中存在4.6.2的要求,因为从GetRSAPrivateKey返回的RSA实例不适用于SignedXml。自那以后(162556年)
2.在不导出的情况下重新打开密钥
一、 我个人不喜欢这种方法,因为它使用(现在是遗留的)PrivateKey属性和RSACryptServiceProvider类。但是,它具有在所有版本的.NET Framework上工作的优势(尽管在非Windows系统上不是.NET Core,因为RSACryptoServiceProvider仅适用于Windows)
如果您已经将cert.PrivateKey强制转换为RSACryptServiceProvider,则可以通过UpgradeCsp发送它。由于这是打开现有密钥,因此不会向磁盘写入额外的材料,因此它使用与现有密钥相同的权限,并且不需要您进行导出
但是(注意!)不要将PersistKeyInCsp=false设置为false,因为这将在克隆关闭时擦除原始密钥。如果在升级到.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();
在dotnet core中,Vladimir Kocjancic的功劳如下:
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.xmldsigrasha256url
作为次要的顺序问题,X509Certificate2.PrivateKey将密钥打开到其导入为的任何提供程序/提供程序类型。恰巧certreq生成ProviderType=1密钥,OpenSSL的默认PFX创建在导入时指定(间接)ProviderType=1存储。我通过使用OpenSSL创建PFX成功地实现了这一效果,如下所示:OpenSSL pkcs12-export-aes256-CSP“Microsoft增强的RSA和AES加密提供程序“-in public.cer-inkey private.key-out combined.pfx
。还要注意的是,我们必须使用MachineKeySet
标志来防止应用程序在每次加载PFX时用文件填充硬盘。仅当您对几个字节的数据进行签名时才有效。更好的方法是使用cert.GetRSAPrivateKey()设置SigningKey
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();