2012-04-30 26 views
5

estoy usando un cliente WCF para hablar con un servicio web no WCF.Especificación de referencia URI de #Body mientras que la firma digital de solicitud SOAP - utilizando WCF

servicio

Esta web requiere que el cuerpo del mensaje SOAP se firmó, sin embargo, estoy teniendo problemas para generar una solicitud SOAP válido.

He implementado un ClientMessageInspector que hereda de IClientMessageInspector, donde modifico el mensaje en el método BeforeSendRequest para agregar la firma digital XML. Uso la clase SignedXML para hacer esto.

estoy usando la herramienta de validación de servicios web de IBM para WSDL y SOAP para comprobar si mi firma digital verifica.

Mi problema es que cuando especifico una referencia completa de espacios de nombres en el URI de referencia, las herramientas de IBM que estoy utilizando decir que tengo una firma válida. A partir de las especificaciones de firma digital XML, solo podría hacer referencia al atributo, sin el espacio de nombres, sin embargo, cuando hago esto, no obtengo una firma digital válida.

Esta es la solicitud SOAP estoy generando actualmente, que mis herramientas dicen que tiene una firma válida, pero el servicio web no le gusta:

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

Esta es la solicitud SOAP Quiero estar generando , pero mis herramientas de decir esto tiene una firma no válida, y el servicio web también me dice que la firma no es válida:

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

y aquí está el código que tengo en BeforeSendRequest para crear la firma, y ​​modificar el mensaje en consecuencia:

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

¿Alguien sabe cómo puedo configurar el URI de referencia en #cuerpo, pero también tengo una firma XML válida?

+0

¿Puedo preguntar por qué está generando la firma manualmente en lugar de usar las características de seguridad de WCF para generarla? ¿Encontró algo que no le permitirá usar la seguridad de WCF? Por cierto. ¿tiene WSDL con WS-Policy o la solicitud válida para el servicio de algún cliente existente? Esos son generalmente artefactos necesarios para construir un cliente que funcione. –

+0

Desafortunadamente, el servicio web no tiene un WSDL. Traté de usar la seguridad WCF primero para lograr esto, pero me encontré con el problema que describo [aquí] (http://stackoverflow.com/questions/10167814/wcf-the-service-certificate-is-not-provided-for- target-error-for-wcf-client). Las soluciones sugeridas no funcionaron, así que pasé a probar una táctica diferente, que es donde estoy ahora. Les pedí a aquellos que hospedan el servicio que me envíen una solicitud válida, pero todavía no la tengo. El servicio al que me refiero no es WCF, así que creo que eso hace que sea más difícil usar WCF OOTB. – Cristy

+0

tengo archivos XSD para el servicio, sin embargo, he utilizado XSD.exe para crear clases de C# de estos, y en mi interfaz de servicio, han puesto la bandera XmlSerializerFormat, que me permite utilizar las clases generadas. – Cristy

Respuesta

2

Como estaba intentando generar una firma para una solicitud SOAP, solucioné esto subclasando SignedXml, anulando GetIdElement, para poder devolver cualquier elemento que esté buscando. En este caso, el elemento de identificación pertenecerá al espacio de nombres 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; 
    } 
} 

también me he dado cuenta de que el uso de herramientas tales como servicios Web de IBM herramienta de validación para WSDL y SOAP para validar la firma digital de la solicitud SOAP doesn' t trabajo. En su lugar, estoy usando el siguiente método para verificar firmas:

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