2012-04-30 7 views
5

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署名もありますか?

+0

なぜ私はあなたのためにWCFセキュリティ機能を使用する代わりに手動で署名を生成しているのですか? WCFセキュリティを使用できないものは見つかりましたか? Btw。 WS-Policyを持つWSDL、または既存のクライアントからのサービスに対する有効な要求がありますか?これらは、通常、作業クライアントを構築するために必要な成果物です。 –

+0

残念ながら、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

+0

サービス用のXSDファイルがありますが、XSD.exeを使用してこれらからC#クラスを作成し、Serviceインターフェイス上で生成されたクラスを使用できるようにXmlSerializerFormatフラグを設定しました。 – Cristy

答えて

2

私はSOAPリクエストのシグネチャを生成しようとしていたので、SignedXmlをサブクラス化してGetIdElementをオーバーライドして、目的の要素を返すことができました。この場合、id要素は、私はまた、SOAPリクエストのデジタル署名を検証するためにWSDLとSOAPのために、このようなIBMのWebサービスの検証ツールなどのツールを使用して」doesnのことを実現しましたhttp://schemas.xmlsoap.org/soap/security/2000-12

public class SignedXmlWithId : SignedXml 
{ 
    public SignedXmlWithId(XmlDocument xml) 
     : base(xml) 
    { 
    } 

    public SignedXmlWithId(XmlElement xmlElement) 
     : base(xmlElement) 
    { 
    } 

    public override XmlElement GetIdElement(XmlDocument doc, string id) 
    { 
     // check to see if it's a standard ID reference 
     XmlElement idElem = base.GetIdElement(doc, id); 

     if (idElem == null) 
     { 
      // I've just hardcoded it for the time being, but should be using an XPath expression here, and the id that is passed in 
      idElem = (XmlElement)doc.GetElementsByTagName("Body", "http://schemas.xmlsoap.org/soap/security/2000-12")[0]; 
     } 

     return idElem; 
    } 
} 

名前空間に属します仕事。代わりに、以下の方法で署名を確認しています。

関連する問題