署名されたXMLドキュメントの署名(.NetのSignedXmlクラス)にプレフィックスを設定する方法はありますか?
したがって、代わりに:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>
私は次のものを持つことができます:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>
署名されたXMLドキュメントの署名(.NetのSignedXmlクラス)にプレフィックスを設定する方法はありますか?
したがって、代わりに:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#>
...
</Signature>
私は次のものを持つことができます:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#>
...
</ds:Signature>
それはできません。署名後にXMLを変更すると、検証できない場合があります。これは、上記の例の場合です。IMOこれは、MSFTのデジタル署名実装の欠陥であり、これに対応する必要があります。
多くの人がこれを行う理由はないと言うでしょう、そして彼らは技術的に正しいです。しかし、巨大なベンダー(つまり、州政府や銀行)と取引している場合は、幸運にも彼らにそれを変更してもらうことができます。ほとんどのリファレンス実装にはそれが含まれています。
更新:署名はSignedInfo要素のすべてに署名するため、事後にその要素を更新すると、署名は無効になります。メッセージを「改ざん」しました。
まず第一に、これを行う正当な理由はありません。2 つの形式は機能的に同等です。正常に動作する XML プロセッサは、それらを完全に同じように処理します。したがって、XML 名前空間を適切に実装していないアプリケーションと通信しようとしている場合を除き、(IMO) デフォルトのフォームをそのままにしておくことをお勧めします。(その場合でも、可能であれば、障害のあるアプリケーションを代わりに修正する方がよいでしょう。)
とはいえ、次のように XPath を使用して、SignedXml.GetXml() とその子要素によって返される XmlElement にプレフィックスを手動で設定できます。
XmlElement signature = signedXml.GetXml();
foreach (XmlNode node in signature.SelectNodes(
"descendant-or-self::*[namespace-uri()='http://www.w3.org/2000/09/xmldsig#']"))
{
node.Prefix = "ds";
}
それは可能ですが、SignedInfo ノードのダイジェスト値を取得する前に、SignedXml クラスを変更してプレフィックスを追加する必要があります。
ComputeSignature メソッドは、prefix パラメーターを追加するように変更されます。
public void ComputeSignature(string prefix){...}
このメソッドが呼び出されると、SignedInfo ノードの値を要約して署名値を計算します。「ds」プレフィックスなしでこの値を取得してからプレフィックスを追加すると、無効な署名が取得されるため、プレフィックスを追加する必要があります。 signedinfo ノードのダイジェスト値を取得する前。
このダイジェスト値はメソッド GetC14NDigest で生成されるため、このメソッドを変更してプレフィックス パラメータを追加し、ダイジェスト値を取得する前にプレフィックスを追加します。
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
XmlElement e = this.SignedInfo.GetXml(); //get the signedinfo nodes
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); /*Set the prefix before getting the HASH*/
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
これで、「ds」プレフィックス付きの SignedInfo ノードの署名値が得られましたが、プレフィックス付きの xml がまだないと言われているため、GetXml メソッドを呼び出すだけで、xml を取得します。もちろん、署名の値は ds プレフィックスを考慮して計算されているため、署名は無効になります。これを回避してプレフィックス付きの xml 構造を取得するには、GetXml メソッドを変更し、プレフィックス パラメータを追加して、Signature Xml のすべてのノードに「ds」プレフィックスを追加する SetPrefix メソッドを呼び出す必要があります。
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e); //return the xml structure with the prefix
return e;
}
これらの変更を加えたクラスをここに残します
カスタムクラス
internal sealed class CustomSignedXml : SignedXml
{
XmlElement obj = null;
public CustomSignedXml (XmlDocument xml)
: base(xml)
{
}
public CustomSignedXml (XmlElement xmlElement)
: base(xmlElement)
{
}
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e);
return e;
}
public void ComputeSignature(string prefix)
{
this.BuildDigestedReferences();
AsymmetricAlgorithm signingKey = this.SigningKey;
if (signingKey == null)
{
throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
if (!(signingKey is DSA))
{
if (!(signingKey is RSA))
{
throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
}
}
else
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
}
}
SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
if (description == null)
{
throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
}
HashAlgorithm hash = description.CreateDigest();
if (hash == null)
{
throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
}
this.GetC14NDigest(hash, prefix);
this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
}
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); //Set the prefix before getting the HASH
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
private void BuildDigestedReferences()
{
Type t = typeof(SignedXml);
MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(this, new object[] { });
}
private void SetPrefix(string prefix, XmlNode node)
{
foreach (XmlNode n in node.ChildNodes)
SetPrefix(prefix, n);
node.Prefix = prefix;
}
}
で、使い方は
CustomSignedXml signedXml = new CustomSignedXml();
.
.//your code
.
//compute the signature with the "ds" prefix
signedXml.ComputeSignature("ds");
//get the xml of the signature with the "ds" prefix
XmlElement xmlDigitalSignature = signedXml.GetXml("ds");