SignedXml 使用 SHA256 计算签名
SignedXml Compute Signature with SHA256
我正在尝试使用 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();
收到 "Invalid algorithm specified." 错误。谁能告诉我我做错了什么?
X509Certificate2
将私钥从 pfx 文件加载到 Microsoft Enhanced Cryptographic Provider v1.0(提供程序类型 1
a.k.a .PROV_RSA_FULL
) 不支持 SHA-256.
基于 CNG 的加密提供程序(在 Vista 和 Server 2008 中引入)支持比基于 CryptoAPI 的提供程序更多的算法,但 .NET 代码似乎仍然与基于 CryptoAPI 的 类 一起工作,例如 RSACryptoServiceProvider
而不是 RSACng
所以我们必须解决这些限制。
但是,另一个 CryptoAPI 提供程序 Microsoft Enhanced RSA and AES Cryptographic Provider(提供程序类型 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。使用 GetRSAPrivateKey 和 .NET 4.6.2(目前处于预览阶段)
GetRSAPrivateKey(扩展)方法 returns "the best available type" 的 RSA 实例用于密钥和平台(相对于 PrivateKey 属性 "everyone knows" returnsRSACryptoServiceProvider).
在 99.99(等)% 的所有 RSA 私钥中,从此方法返回的对象能够生成 SHA-2 签名。
虽然在 .NET 4.6(.0) 中添加了该方法,但在这种情况下存在 4.6.2 的要求,因为从 GetRSAPrivateKey 返回的 RSA 实例不适用于 SignedXml。自 been fixed (162556).
2。重新打开密钥不导出
我个人不喜欢这种方法,因为它使用(现在是旧版)PrivateKey 属性 和 RSACryptoServiceProvider class。但是,它具有在所有版本的 .NET Framework 上工作的优势(尽管在非 Windows 系统上不是 .NET Core,因为 RSACryptoServiceProvider 是 Windows-only)。
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 发送它。由于这是打开一个现有的密钥,因此不会有额外的 material 写入磁盘,它使用与现有密钥相同的权限,并且不需要您进行导出。
但是(注意!)不要设置 PersistKeyInCsp=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();
感谢 Vladimir Kocjancic
在 dotnet 核心中我有这个:
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
我正在尝试使用 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();
收到 "Invalid algorithm specified." 错误。谁能告诉我我做错了什么?
X509Certificate2
将私钥从 pfx 文件加载到 Microsoft Enhanced Cryptographic Provider v1.0(提供程序类型 1
a.k.a .PROV_RSA_FULL
) 不支持 SHA-256.
基于 CNG 的加密提供程序(在 Vista 和 Server 2008 中引入)支持比基于 CryptoAPI 的提供程序更多的算法,但 .NET 代码似乎仍然与基于 CryptoAPI 的 类 一起工作,例如 RSACryptoServiceProvider
而不是 RSACng
所以我们必须解决这些限制。
但是,另一个 CryptoAPI 提供程序 Microsoft Enhanced RSA and AES Cryptographic Provider(提供程序类型 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。使用 GetRSAPrivateKey 和 .NET 4.6.2(目前处于预览阶段)
GetRSAPrivateKey(扩展)方法 returns "the best available type" 的 RSA 实例用于密钥和平台(相对于 PrivateKey 属性 "everyone knows" returnsRSACryptoServiceProvider).
在 99.99(等)% 的所有 RSA 私钥中,从此方法返回的对象能够生成 SHA-2 签名。
虽然在 .NET 4.6(.0) 中添加了该方法,但在这种情况下存在 4.6.2 的要求,因为从 GetRSAPrivateKey 返回的 RSA 实例不适用于 SignedXml。自 been fixed (162556).
2。重新打开密钥不导出
我个人不喜欢这种方法,因为它使用(现在是旧版)PrivateKey 属性 和 RSACryptoServiceProvider class。但是,它具有在所有版本的 .NET Framework 上工作的优势(尽管在非 Windows 系统上不是 .NET Core,因为 RSACryptoServiceProvider 是 Windows-only)。
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 发送它。由于这是打开一个现有的密钥,因此不会有额外的 material 写入磁盘,它使用与现有密钥相同的权限,并且不需要您进行导出。
但是(注意!)不要设置 PersistKeyInCsp=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();
感谢 Vladimir Kocjancic
在 dotnet 核心中我有这个:
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