我正在尝试使用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();出错。有人能告诉我我做错了什么吗?
发布于 2015-03-14 13:25:01
X509Certificate2将私钥从pfx文件加载到MicrosoftExancedProcessicalProviderv1.0(提供程序类型为1 a.k.a )中。PROV_RSA_FULL),它不支持SHA-256.
基于CNG的密码提供者(在Vista和Server 2008中引入)比基于CryptoAPI的提供程序支持更多的算法,但是.NET代码似乎仍然在使用基于CryptoAPI的类,比如RSACryptoServiceProvider,而不是RSACng,所以我们必须克服这些限制。
但是,另一个CryptoAPI提供程序,微软增强了RSA和AES密码提供程序(提供程序类型24 a.k.a )。PROV_RSA_AES)确实支持SHA-256.所以如果我们把私钥拿到这个提供者那里,我们就可以用它签名了。
首先,您必须调整您的X509Certificate2构造函数,以便通过添加X509KeyStorageFlags.Exportable标志将密钥导出到X509Certificate2将其放入的提供者之外:
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上的提供程序类型及其代码列表。
下面是您的示例的完整调整代码:
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();发布于 2016-07-14 17:44:03
导出和重新导入已经有了作为回答,但是还有几个其他选项您应该知道。
1.使用GetRSAPrivateKey和.NET 4.6.2 (目前正在预览)
GetRSAPrivateKey (扩展)方法返回密钥和平台的“最佳可用类型”的RSA实例(与“每个人都知道”返回RSACryptoServiceProvider的PrivateKey属性相反)。
在99.99(etc)%的所有RSA私钥中,该方法返回的对象能够完成SHA-2签名生成。
虽然该方法是在.NET 4.6(.0)中添加的,但在这种情况下存在4.6.2的要求,因为从GetRSAPrivateKey返回的RSA实例不适用于SignedXml。这已经从固定的 (162556)开始。
2.重新打开钥匙而不出口
就我个人而言,我不喜欢这种方法,因为它使用了(现在的遗留) PrivateKey属性和RSACryptoServiceProvider类。但是,它的优点是可以在所有版本的.NET框架上工作(但在非Windows系统上不使用.NET核心,因为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,因为当克隆关闭时,它会擦除原来的键。
发布于 2020-11-19 15:35:32
如果您在升级到.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();Vladimir 学分
https://stackoverflow.com/questions/29005876
复制相似问题