SignedXml Compute Signature with SHA256
我正在尝试使用SHA256对XML文档进行数字签名。
我正在尝试为此使用Security.Cryptography.dll。
这是我的代码-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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(); |
但是我收到"指定了无效的算法"。
基于CNG的密码提供程序(在Vista和Server 2008中引入)比基于CryptoAPI的提供程序支持更多算法,但是.NET代码似乎仍可与基于CryptoAPI的类(如
但是,另一个CryptoAPI提供程序Microsoft增强型RSA和AES加密提供程序(提供程序类型
首先,您必须调整
1 2 3 | X509Certificate2 cert = new X509Certificate2( @"location of pks file","password", X509KeyStorageFlags.Exportable); |
并导出私钥:
1 2 | var exportedKeyMaterial = cert.PrivateKey.ToXmlString( /* includePrivateParameters = */ true); |
然后为支持SHA-256的提供者创建一个新的
1 2 3 |
并将私钥导入其中:
1 | key.FromXmlString(exportedKeyMaterial); |
创建
1 | signedXml.SigningKey = key; |
它现在将起作用。
这是MSDN上提供程序类型及其代码的列表。
这是您的示例的完整调整后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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(当前处于预览状态)
GetRSAPrivateKey(扩展名)方法为密钥和平台返回"最佳类型"的RSA实例(与"每个人都知道"的私钥属性返回RSACryptoServiceProvider相对)。
在所有RSA私钥的99.99%等中,此方法返回的对象能够进行SHA-2签名生成。
在.NET 4.6(.0)中添加了该方法后,在这种情况下仍存在4.6.2的要求,因为从GetRSAPrivateKey返回的RSA实例不适用于SignedXml。此后已解决(162556)。
2.重新打开密钥而不导出
我个人不喜欢这种方法,因为它使用了(现在是传统)PrivateKey属性和RSACryptoServiceProvider类。但是,它具有可以在所有版本的.NET Framework上工作的优势(尽管非Windows系统上不是.NET Core,因为RSACryptoServiceProvider仅限于Windows)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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,因为在关闭克隆时,这将擦除原始密钥。