簡體   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