2012-04-30 26 views
5

Używam klienta WCF do rozmawiania z usługą inną niż WCF.Określanie odniesienia URI #Body podczas cyfrowego podpisywania żądania SOAP - przy użyciu WCF

Ta usługa WWW wymaga podpisania treści komunikatu SOAP, ale mam problem z wygenerowaniem prawidłowego żądania SOAP.

Zaimplementowałem ClientMessageInspector, który dziedziczy po IClientMessageInspector, gdzie modyfikuję komunikat w metodzie BeforeSendRequest w celu dodania podpisu cyfrowego XML. W tym celu używam klasy SignedXML.

Używam narzędzia sprawdzania poprawności IBM Web Services dla WSDL i SOAP w celu sprawdzenia, czy mój podpis cyfrowy weryfikuje.

Mój problem polega na tym, że kiedy podaję referencje pełnego referencyjnego identyfikatora w referencyjnym identyfikatorze URI, narzędzia IBM, których używam, mówią, że mam prawidłowy podpis. Ze specyfikacji XML Digital Signature powinieneś móc odwoływać się do atrybutu bez przestrzeni nazw, jednak gdy to zrobię, nie otrzymam prawidłowego podpisu cyfrowego.

To żądanie SOAP Jestem obecnie generowania, co mówią moje narzędzia ma prawidłowy podpis, ale usługa internetowa nie podoba:

<?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> 

To żądanie SOAP Chcę być generowanie , ale moje narzędzia powiedzieć to ma nieprawidłowy podpis, a serwis internetowy mówi mi też podpis jest nieważny:

<?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> 

a oto kod mam w BeforeSendRequest do utworzenia podpisu i modyfikowania wiadomości odpowiednio:

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; 
    } 

Czy ktoś wie, w jaki sposób ustawić referencyjny identyfikator URI na #Body, ale także mieć prawidłowy podpis XML?

+0

Czy mogę zapytać, dlaczego generowanie podpisu ręcznie, zamiast korzystać z funkcji zabezpieczeń WCF do generowania go dla Ciebie? Czy znalazłeś coś, co nie pozwoli ci używać zabezpieczeń WCF? Btw. masz WSDL z WS-Policy lub prawidłowe żądanie usługi od jakiegoś istniejącego klienta? Są to zazwyczaj niezbędne artefakty, aby zbudować działającego klienta. –

+0

Niestety usługa internetowa nie ma WSDL. Próbowałem użyć zabezpieczeń WCF pierwszy, aby to osiągnąć, ale miałem problem, który opisuję [tutaj] (http://stackoverflow.com/questions/10167814/wcf-the-service-certificate-is-not-provided-for- target-error-for-wcf-client). Sugerowane rozwiązania nie zadziałały, więc zacząłem próbować innej taktyki, w której teraz jestem. Poprosiłem tych, którzy obsługują usługę, o przesłanie mi ważnej prośby, ale jeszcze jej nie mam. Usługa, z którą rozmawiam, nie jest WCF, więc myślę, że utrudnianie korzystania z WCF OOTB. – Cristy

+0

Mam pliki XSD dla usługi, ale użyłem XSD.exe do tworzenia z nich klas C#, a na moim interfejsie serwisowym ustawiłem flagę XmlSerializerFormat, aby umożliwić mi korzystanie z wygenerowanych klas. – Cristy

Odpowiedz

2

Ponieważ próbowałem wygenerować sygnaturę dla żądania SOAP, obejrzałem to poprzez podklasowanie SignedXml, nadpisując GetIdElement, dzięki czemu mogę zwrócić dowolny element, którego szukam. W tym przypadku element id będzie należeć do przestrzeni nazw 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; 
    } 
} 

ja też sobie sprawę, że przy użyciu narzędzi takich jak IBM Web Services Validation Tool dla WSDL i SOAP do sprawdzania podpisu cyfrowego żądania SOAP nie robi” t działa. Zamiast tego używam następującą metodę do sprawdzania podpisów:

public static bool verifyDigitalSignatureForString(string msgAsString) 
    { 
     XmlDocument verifyDoc = new XmlDocument(); 
     verifyDoc.PreserveWhitespace = true; 
     verifyDoc.LoadXml(msgAsString); 

     SignedXmlWithId verifyXml = new SignedXmlWithId(verifyDoc); 
     // Find the "Signature" node and create a new 
     // XmlNodeList object. 
     XmlNodeList nodeList = verifyDoc.GetElementsByTagName("Signature"); 

     // Load the signature node. 
     verifyXml.LoadXml((XmlElement)nodeList[0]); 

     if (verifyXml.CheckSignature()) 
     { 
      Console.WriteLine("Digital signature is valid"); 
      return true; 
     } 
     else 
     { 
      Console.WriteLine("Digital signature is not valid"); 
      return false; 
     } 
    } 
Powiązane problemy