サードパーティのWebサービスから応答があります。その応答でXmlDocumentをロードします。
_ string txt = readStream.ReadToEnd();
response = new XmlDocument();
response.PreserveWhitespace = true;
response.LoadXml(txt);
return response;
_
ここで、証明書を使用して応答が署名されていることを確認したいと思います。 msdn で見つけたVerifyXmlDoc(XmlDocument xmlDoc)
メソッドがあります。
私はメッセージが正しいことを知っています。
_ public bool VerifyXmlDoc(XmlDocument xmlDoc)
{
SignedXml signed = new SignedXml(xmlDoc);
XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");
signed.LoadXml((XmlElement)signatureNodeList[0]);
X509Certificate2 serviceCertificate = null;
foreach (KeyInfoClause clause in signed.KeyInfo)
{
if (clause is KeyInfoX509Data)
{
if (((KeyInfoX509Data)clause).Certificates.Count > 0)
{
serviceCertificate = (X509Certificate2)((KeyInfoX509Data)clause).Certificates[0];
}
}
}
bool result = signed.CheckSignature(serviceCertificate, true);
return result;
}
_
プロジェクトのターゲットフレームワークを.NET3.5または.NET3、または.NET 2に設定すると、うまく機能します。結果は本当です。しかし、ターゲットフレームワークを.NET 4に変更すると、結果はfalseになります。 (そして私は.NET 4を使用する必要があります)
この問題を解決する方法についてのアイデアはありますか?
これは既知の問題です。 .NET3.5と.NET4.0の間の正規化の実装が変更されました。
これがすべてのXML署名で機能するかどうかはわかりませんが、私が行ったテストから次のように機能します。
次のC14NTransformクラスをプロジェクトに追加します。
public class MyXmlDsigC14NTransform: XmlDsigC14NTransform {
static XmlDocument _document;
public static XmlDocument document {
set {
_document = value;
}
}
public MyXmlDsigC14NTransform() {}
public override Object GetOutput() {
return base.GetOutput();
}
public override void LoadInnerXml(XmlNodeList nodeList) {
base.LoadInnerXml(nodeList);
}
protected override XmlNodeList GetInnerXml() {
XmlNodeList nodeList = base.GetInnerXml();
return nodeList;
}
public XmlElement GetXml() {
return base.GetXml();
}
public override void LoadInput(Object obj) {
int n;
bool fDefaultNS = true;
XmlElement element = ((XmlDocument) obj).DocumentElement;
if (element.Name.Contains("SignedInfo")) {
XmlNodeList DigestValue = element.GetElementsByTagName("DigestValue", element.NamespaceURI);
string strHash = DigestValue[0].InnerText;
XmlNodeList nodeList = _document.GetElementsByTagName(element.Name);
for (n = 0; n < nodeList.Count; n++) {
XmlNodeList DigestValue2 = ((XmlElement) nodeList[n]).GetElementsByTagName("DigestValue", ((XmlElement) nodeList[n]).NamespaceURI);
string strHash2 = DigestValue2[0].InnerText;
if (strHash == strHash2) break;
}
XmlNode node = nodeList[n];
while (node.ParentNode != null) {
XmlAttributeCollection attrColl = node.ParentNode.Attributes;
if (attrColl != null) {
for (n = 0; n < attrColl.Count; n++) {
XmlAttribute attr = attrColl[n];
if (attr.Prefix == "xmlns") {
element.SetAttribute(attr.Name, attr.Value);
} else if (attr.Name == "xmlns") {
if (fDefaultNS) {
element.SetAttribute(attr.Name, attr.Value);
fDefaultNS = false;
}
}
}
}
node = node.ParentNode;
}
}
base.LoadInput(obj);
}
}
CryptoConfig.AddAlgorithmメソッドを使用してクラスを登録します。
CryptoConfig.AddAlgorithm(typeof(MyXmlDsigC14NTransform), "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
var message = new XmlDocument();
message.PreserveWhitespace = true;
message.Load("XmlSig.xml");
MyXmlDsigC14NTransform.document = message; // The transform class needs the xml document
// Validate signature as normal.
それはそれをする必要があります。
SignedXmlクラスのSignedInfoプロパティのCanonicalizationメソッドを明示的に設定してみてください。Net2.0と.Net4.0の間でデフォルトの動作に変更があったようです。
signed.SignedInfo.CanonicalizationMethod = Signed.XmlDsigExcC14NTransformUrl;
参照:
私は同じ問題を抱えていましたが、それらの答えのどれも私を助けませんでした。この場合、.Netバージョンではなく、使用したオペレーティングシステムに応じて機能するかどうかが決まります。
App.configにこのコードを追加して、SignedXMLログを有効にし、背後で何が起こったかを確認しました。
<system.diagnostics>
<sources>
<source name="System.Security.Cryptography.Xml.SignedXml" switchName="XmlDsigLogSwitch">
<listeners>
<add name="logFile" />
</listeners>
</source>
</sources>
<switches>
<add name="XmlDsigLogSwitch" value="Verbose" />
</switches>
<sharedListeners>
<add name="logFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="XmlDsigLog.txt"/>
</sharedListeners>
<trace autoflush="true">
<listeners>
<add name="logFile" />
</listeners>
</trace>
</system.diagnostics>
それはこの特定の行を書いた:
System.Security.Cryptography.Xml.SignedXml Information: 17 : [SignedXml#033ec00f, UnsafeTransformMethod] Canonicalization method "http://www.w3.org/TR/1999/REC-xpath-19991116" is not on the safe list. Safe canonicalization methods are: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", "http://www.w3.org/2001/10/xml-exc-c14n#", "http://www.w3.org/2001/10/xml-exc-c14n#WithComments", "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2000/09/xmldsig#base64", "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform", "http://www.w3.org/2002/07/decrypt#XML".
セキュリティアップデート3141780によって発生したエラーを修正しようとするこのMicrosoftサポートの記事を見つけました: https://support.Microsoft.com/en-us/kb/3148821
その記事のシナリオ2セクションには、2つの解決策があり、XPath変換メソッドに関連するレジストリキーを適用する際の問題を修正しました:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\Security\SafeTransformMethods @ XmlDsigXPathTransform = http:/ /www.w3.org/TR/1999/REC-xpath-19991116
// Assume the data to sign is in the data.xml file, load it, and
// set up the signature object.
XmlDocument doc = new XmlDocument();
doc.Load(@"D:\Example.xml");
SignedXml sig = new SignedXml(doc);
// Make a random RSA key, and set it on the signature for signing.
RSA key = new RSACryptoServiceProvider();
sig.SigningKey = key;
// Create a Reference to the containing document, add the enveloped
// transform, and then add the Reference to the signature
Reference refr = new Reference("");refr.AddTransform(new XmlDsigEnvelopedSignatureTransform());
sig.AddReference(refr);
// Compute the signature, add it to the XML document, and save
sig.ComputeSignature();
doc.DocumentElement.AppendChild(sig.GetXml());
doc.Save("data-signed.xml");
// Load the signed data
//XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load("data-signed.xml");
// Find the Signature element in the document
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
XmlElement sigElt = (XmlElement)doc.SelectSingleNode("//dsig:Signature", nsm);
// Load the signature for verification
//SignedXml sig = new SignedXml(doc);
sig.LoadXml(sigElt);
// Verify the signature, assume the public key part of the
// signing key is in the key variable
if (sig.CheckSignature(key))
Console.WriteLine("Signature verified");
else
Console.WriteLine("Signature not valid");
NET 4.0以降で署名を確認するには、CanonicalizationMethodのコンテキストを変更する必要があるため、signedXmlオブジェクトを次の方法で初期化する必要があります。
XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");
SignedXml signedXml = new SignedXml((XmlElement)signatureNodeList[0]);
signedXml.LoadXml((XmlElement)signatureNodeList[0]);
//Then you proceed your check as usual
この問題を解決し、SignatureタグからSignedInfoに同じ名前空間を追加しました。このような:
前:
後:
.NET Framework 4/4.5以降、x509証明書およびその他のセキュリティ機能を処理するクラスはSystem.IdentityModel.dllにあります。上記の名前空間でそれぞれのクラスを検索します。