WCFクライアントを使用して、WCF以外のWebサービスと通信しています。SOAP要求にデジタル署名する際に#Bodyの参照URIを指定する - WCFを使用する
このWebサービスでは、SOAPメッセージの本文に署名が必要ですが、有効なSOAPリクエストの生成に問題があります。
IClientMessageInspectorを継承するClientMessageInspectorを実装しました。そこで、BeforeSendRequestメソッドのメッセージを変更してXMLデジタル署名を追加します。私はこれを行うにはSignedXMLクラスを使用します。
WSDLとSOAP用のIBM Web Services Validation Toolを使用して、自分のデジタル署名が検証されているかどうかを確認しています。
私の問題は、参照URIに完全な名前空間の参照を指定すると、私が使用しているIBMのツールで有効な署名があると言われています。 XMLデジタル署名の仕様からは、ネームスペースなしで属性を参照するだけでよいはずですが、これを行うと有効なデジタル署名が得られません。
これは私が現在、私のツールが有効な署名がありますが、Webサービスがそれを好きではないと言っている、生成していますSOAPリクエストです:
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:soapsec="http://schemas.xmlsoap.org/soap/security/2000-12"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Header>
<soapsec:Signature>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="http://schemas.xmlsoap.org/soap/security/2000-12#Body">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>4mt5wluUTu5tpR2d5UemVSLvqTs=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>UZ7HzfE3GxIY9hg...</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIEkTCCA3mgAwIBAgIQCu...</X509Certificate>
</X509Data>
<KeyValue>
<RSAKeyValue>
<Modulus>0C3e9HDx5Yq6FLUxIgjJ...</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</soapsec:Signature>
</s:Header>
<s:Body soapsec:id="Body">
.... SOAP Body Here ...
</s:Body>
</s:Envelope>
これは私が作成したいSOAPリクエストであります
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:soapsec="http://schemas.xmlsoap.org/soap/security/2000-12"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<s:Header>
<soapsec:Signature>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#Body">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>4mt5wluUTu5tpR2d5UemVSLvqTs=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>UZ7HzfE3GxIY9hg...</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIEkTCCA3mgAwIBAgIQCu...</X509Certificate>
</X509Data>
<KeyValue>
<RSAKeyValue>
<Modulus>0C3e9HDx5Yq6FLUxIgjJ...</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</soapsec:Signature>
</s:Header>
<s:Body soapsec:id="Body">
.... SOAP Body Here ...
</s:Body>
</s:Envelope>
そしてここでは、署名を作成し、それに応じてメッセージを変更するBeforeSendRequestで私が持っているコードです。が、私のツールは、これは無効な署名を持っており、ウェブサービスにも署名が無効である私に語ったと言います。
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.LoadXml(request.ToString());
// Add the required namespaces to the SOAP Envelope element, if I don't do this, the web service I'm calling returns an error
string soapSecNS = "http://schemas.xmlsoap.org/soap/security/2000-12";
string soapEnvNS = "http://www.w3.org/2003/05/soap-envelope";
//Get the header element, so that we can add the digital signature to it
XmlNode headerNode = doc.GetElementsByTagName("Header", soapEnvNS)[0];
// Set the ID attribute on the body element, so that we can reference it later
XmlNode bodyNode = doc.GetElementsByTagName("Body", soapEnvNS)[0];
((XmlElement)bodyNode).RemoveAllAttributes();
((XmlElement)bodyNode).SetAttribute("id", soapSecNS, "Body");
XmlWriterSettings settings2 = new XmlWriterSettings();
settings2.Encoding = new System.Text.UTF8Encoding(false);
// Load the certificate we want to use for signing
SignedXmlWithId signedXml = new SignedXmlWithId(doc);
X509Certificate2 cert = new X509Certificate2("C:\\myCertificate.pfx", "myPassword");
signedXml.SigningKey = cert.PrivateKey;
//Populate the KeyInfo element correctly, with the public cert and public key
Signature sigElement = signedXml.Signature;
KeyInfoX509Data x509Data = new KeyInfoX509Data(cert);
sigElement.KeyInfo.AddClause(x509Data);
RSAKeyValue rsaKeyValue = new RSAKeyValue((RSA)cert.PublicKey.Key);
sigElement.KeyInfo.AddClause(rsaKeyValue);
// Create a reference to be signed, only sign the body of the SOAP request, which we have given an
// ID attribute to, in order to reference it correctly here
Reference reference = new Reference();
reference.Uri = soapSecNS + "#Body";
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
XmlElement soapSignature = doc.CreateElement("Signature", soapSecNS);
soapSignature.Prefix = "soapsec";
soapSignature.AppendChild(xmlDigitalSignature);
headerNode.AppendChild(soapSignature);
// Make sure the byte order mark doesn't get written out
XmlDictionaryReaderQuotas quotas = new XmlDictionaryReaderQuotas();
Encoding encoderWithoutBOM = new System.Text.UTF8Encoding(false);
System.IO.MemoryStream ms = new System.IO.MemoryStream(encoderWithoutBOM.GetBytes(doc.InnerXml));
XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(ms, encoderWithoutBOM, quotas, null);
//Create the new message, that has the digital signature in the header
Message newMessage = Message.CreateMessage(xdr, System.Int32.MaxValue, request.Version);
request = newMessage;
return null;
}
参照URIを#Bodyに設定する方法はわかりますが、有効なXML署名もありますか?
なぜ私はあなたのためにWCFセキュリティ機能を使用する代わりに手動で署名を生成しているのですか? WCFセキュリティを使用できないものは見つかりましたか? Btw。 WS-Policyを持つWSDL、または既存のクライアントからのサービスに対する有効な要求がありますか?これらは、通常、作業クライアントを構築するために必要な成果物です。 –
残念ながら、WebサービスにはWSDLがありません。私はこれを達成するためにWCFのセキュリティを最初に使ってみましたが、私が[ここに記述する]問題に走っていました(http://stackoverflow.com/questions/10167814/wcf-the-service-certificate-is-not-provided-for- target-error-for-wcf-client)を参照してください。提案された解決策はうまくいかなかったので、私は今ここにいる別の戦術を試してみることに移った。私はサービスを主催する人に私に有効な要求を送るように頼んだが、私はまだそれを持っていない。私が話しているサービスはWCFではないので、WCF OOTBを使用するのが難しくなっていると思います。 – Cristy
サービス用のXSDファイルがありますが、XSD.exeを使用してこれらからC#クラスを作成し、Serviceインターフェイス上で生成されたクラスを使用できるようにXmlSerializerFormatフラグを設定しました。 – Cristy