繁体   English   中英

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();处出错signedXml.ComputeSignature(); . 谁能告诉我我做错了什么?

X509Certificate2将私钥从 pfx 文件加载到不支持 SHA-256 的Microsoft Enhanced Cryptographic Provider v1.0 (provider type 1 aka PROV_RSA_FULL )中。

基于 CNG 的加密提供程序(在 Vista 和 Server 2008 中引入)比基于 CryptoAPI 的提供程序支持更多的算法,但是 .NET 代码似乎仍然使用基于 CryptoAPI 的类,例如RSACryptoServiceProvider而不是RSACng因此我们必须解决这些限制。

但是,另一个 CryptoAPI 提供程序, Microsoft Enhanced RSA 和 AES Cryptographic Provider (提供程序类型24又名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();

导出和重新导入已作为答案给出,但您应该注意其他几个选项。

1. 使用 GetRSAPrivateKey 和 .NET 4.6.2(目前为预览版)

GetRSAPrivateKey (扩展)方法为密钥和平台返回“最佳可用类型”的 RSA 实例(而不是“每个人都知道”的 PrivateKey 属性返回 RSACryptoServiceProvider)。

在 99.99(etc)% 的所有 RSA 私钥中,此方法返回的对象能够生成 SHA-2 签名。

虽然该方法是在 .NET 4.6(.0) 中添加的,但在这种情况下存在 4.6.2 的要求,因为从 GetRSAPrivateKey 返回的 RSA 实例不适用于 SignedXml。 此后已修复(162556)。

2. 重新打开key不导出

我个人不喜欢这种方法,因为它使用(现在是遗留的) 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,因为这会在克隆关闭时删除原始密钥。

如果您在升级到 .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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM