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();
でエラーが発生しました。誰かが私が間違っていることを教えてもらえますか?
X509Certificate2
は、pfxファイルからMicrosoft Enhanced Cryptographic Provider v1.0(プロバイダータイプ1
aka PROV_RSA_FULL
)SHA-256をサポートしていません。
CNGベースの暗号化プロバイダー(VistaおよびServer 2008で導入)は、CryptoAPIベースのプロバイダーよりも多くのアルゴリズムをサポートしていますが、.NETコードは、RSACryptoServiceProvider
ではなくRSACng
などのCryptoAPIベースのクラスで動作しているようなので、回避する必要があります。これらの制限。
ただし、別のCryptoAPIプロバイダーMicrosoft Enhanced RSAおよびAES暗号化プロバイダー(プロバイダータイプ24
aka 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
インスタンスを作成したら、cert.PrivateKey
ではなくkey
を使用するように指示します。
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();
エクスポートと再インポートはすでに 回答として与えられています ですが、他にもいくつか知っておくべきオプションがあります。
GetRSAPrivateKey (拡張)メソッドは、キーとプラットフォームの「利用可能な最良のタイプ」のRSAインスタンスを返します(「誰もが知っている」PrivateKeyプロパティがRSACryptoServiceProviderを返すのとは対照的)。
すべてのRSA秘密鍵の99.99(etc)%で、このメソッドから返されたオブジェクトはSHA-2署名を生成できます。
このメソッドは.NET 4.6(.0)で追加されましたが、この場合は4.6.2の要件が存在します。これは、GetRSAPrivateKeyから返されたRSAインスタンスがSignedXmlで機能しなかったためです。それは 固定される (162556)以来です。
私は個人的に、このアプローチは(現在はレガシー)PrivateKeyプロパティとRSACryptoServiceProviderクラスを使用しているため、好きではありません。ただし、これには.NET Frameworkのすべてのバージョンで作業できるという利点があります(ただし、RSACryptoServiceProviderはWindows専用であるため、Windows以外のシステムでは.NET Coreではありません)。
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);
}
RSACryptoServiceProviderとしてcert.PrivateKeyキャストを既に持っている場合は、UpgradeCspを介して送信できます。これにより既存のキーが開かれるため、ディスクに書き込まれる余分な情報はありません。既存のキーと同じ権限を使用し、エクスポートを行う必要はありません。
ただし、(注意!)PersistKeyInCsp = falseを設定しないでください。これにより、クローンが閉じられたときに元のキーが消去されます。