2011-01-12 193 views
5

Es mi primera pregunta en la web. Espero que tenga sentido.Firma de mensajes SOAP utilizando el certificado X.509 del servicio WCF al servicio web Java

He visto varios blogs relacionados con este tema en la Web, y he intentado algunas de las ideas presentadas en ellos sin éxito. Aquí está mi situación:

Tengo una aplicación web llamando a un servicio web WCF que luego llama a un servicio web Java. Todos están en diferentes servidores. La llamada entre el servicio web de WCF al servicio web de Java no supera el https, ya que el certificado será suficiente para identificar al que llama (por lo tanto, la seguridad del mensaje).

  • servicio web Java (cuadro negro)

El servicio web Java requiere que recibió un mensaje firmado y trabaja según abajo:
Antes de cada solicitud se procesa un manejador intercepta todos los mensajes entrantes y realiza las siguientes reglas de validación:
1. ¿El mensaje contiene un encabezado de seguridad
2. El mensaje contiene el ID de encabezado de seguridad correcto
3. ¿Es el mensaje ha firmado correctamente
4. ¿El mensaje contiene un certificado X.509 KeyInfo
5. ¿Es el certificado emitido por una CA de confianza - configuración basada
6. ¿El certificado válido (no caducado, revocado)
7. ¿El certificado contiene la política correcta OID

Una vez que se hayan confirmado todos estos pasos, el mensaje puede procesarse; si falla algún paso, se devolverá una excepción de mensaje de jabón.

El encabezado de seguridad SOAP debe validar contra xxx ... w3.org/TR/SOAP-dsig/ especificación de firma digital.

La descripción más completa se puede encontrar aquí xxx ... ibm.com/developerworks/webservices/library/ws-security.html este artículo de IBM enumera los detalles de cada encabezado de WS-Security, además de un mensaje SOAP firmado de muestra ha sido proporcionado.

Al firmar el mensaje SOAP, también debe agregar el certificado x.509 en el mensaje KeyInfo, que es necesario para la validación del certificado.

Solicitud SOAP debería desea:

<?xml version="1.0" encoding="UTF-8"?> 
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> 
<S:Header> 
<ds:Signature xmlns:ds="xxx...w3.org/2000/09/xmldsig#" Id="Signature001"> 
<ds:SignedInfo> 
<ds:CanonicalizationMethod Algorithm="xxx...w3.org/TR/2001/REC-xml-c14n-20010315"/> 
<ds:SignatureMethod Algorithm="xxx...w3.org/2000/09/xmldsig#rsa-sha1"/> 
<ds:Reference URI=""> 
<ds:Transforms> 
<ds:Transform Algorithm="xxx...w3.org/2000/09/xmldsig#enveloped-signature"/> 
</ds:Transforms> 
<ds:DigestMethod Algorithm="xxx...w3.org/2000/09/xmldsig#sha1"/> 
<ds:DigestValue>soe1PnaGXVGrsauC61JSHD+uqGw=</ds:DigestValue> 
</ds:Reference> 
<ds:Reference URI="#KeyInfo001"> 
<ds:DigestMethod Algorithm="xxx...w3.org/2000/09/xmldsig#sha1"/> 
<ds:DigestValue>Y9SRPQ9TcDu+GazO3LFwodEdhaA=</ds:DigestValue> 
</ds:Reference> 
</ds:SignedInfo> 
<ds:SignatureValue>jBX/8XkY2aCte7qgXEp1sbNWmQcK/90iVL58sAvwYAEcBABGzOk2agxR0HvWrNa6ixkocAQ205lggwOxnxZJvoVozVYAAjcLtayPBOUYrnSEBFrwKWP/vxgvUDRIdXeIuw5GLY87NrTQMm1Ehf/HvMX9hTBJn4Nm8RdDiUmPcIo=</ds:SignatureValue> 
<ds:KeyInfo Id="KeyInfo001"> 
<ds:X509Data> 
<ds:X509Certificate>MIIEbZCCA1WgAwIBAgIES1XpMjANBgkqhkiG9w0BAQUFADBYMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTAeFw0xMDA0MjIxMDQ4MDBaFw0xMzA0MjIxMTE4MDBaMGoxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDEXMBUGCgmSJomT8ixkARkWB2VudHJ1c3QxEjAQBgNVBAMTCWVudHJ1c3RTTTESMBAGA1UEAxMJZW50cnVzdENBMRAwDgYDVQQDEwdSYnMgUmJzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMf88L2JjLPG1hNmTA/KBiC53WVwS2WU9Jh3lC1Rob6RMzOojomZ/dNrvSRB6nzWeXJpZXwik4XFrsAq24By2SZpLTO4p8Vcq71mTAfDu33cnO49Au2pwNvcMn5qIKBk1Xx+oVb4fzK9ncTRu7bW46HsIYth+qkGhbI2JEHwr/zwIDAQABo4IBrzCCAaswCwYDVR0PBAQDAgeAMCsGA1UdEAQkMCKADzIwMTAwNDIyMTA0ODAwWoEPMjAxMjA1MjgxNTE4MDBaMCMGA1UdIAQcMBowCwYJYIZIAYb6awoEMAsGCSqGSIb2fQdLAzAbBgNVHQkEFDASMBAGCSqGSIb2fQdEHTEDAgEBMIHGBgNVHR8Egb4wgbswb6BtoGukaTBnMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTENMAsGA1UEAxMEQ1JMMTBIoEagRIZCZmlsZTovLy8vTVNJREhVLTQ0NUE0RkVFL0NSTC9lbnRydXN0Y2FfZW50cnVzdHNtX2xvY2FsX2NybGZpbGUuY3JsMB8GA1UdIwQYMBaAFBvSL6cPz8L5shubV58yf0pczKzuMB0GA1UdDgQWBBT1/j6OSS8FTjwqluvew16sv7h+VzAJBgNVHRMEAjAAMBkGCSqGSIb2fQdBAAQMMAobBFY4LjADAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQBXxRIA4HUvGSw4L+4uaR51pY4ISjUQWo2Fh7FYBMt29NsKCTdur1OWVVdndt1yjXP4yWXxoAhHtvZL+XNALUFlR2HAWiXuL1nRcxHkB98N5gPqQzW/lJk9cLtL4hVp28EiEpgmKT3I3NP2Pdb2G5MMOdvQ/GFb2y6OwblR8ViPQ8B2aHWzXMrH+0qadPAuBhXyAohwb+mMuYT/ms6xpGi1NMYuYMf6XONz9GkZgnGnMwa+9CCQws1HNz8WYHtmFIxLsVuEWc/0a1vg4IYX1Ds/ttyhJGTVXOSJSkBz8kRyj1pNBDdc1KeG8M++O8m8VgRTJvYaPc7NMiclISukGpea</ds:X509Certificate> </ds:X509Data> 
</ds:KeyInfo> 
</ds:Signature> 
</S:Header> 
<S:Body Id="ABC"> 
<ns2:createUser xmlns:ns2="http://webservice.rbs.emea.ps.entrust.com/" xmlns:ns3="http://webservice.rbs.emea.ps.entrust.com/types/CertificateException" xmlns:ns4="http://webservice.rbs.emea.ps.entrust.com/types/UserException"> 
<userID>0061020051</userID> 
</ns2:createUser> 
</S:Body> 
</S:Envelope> 
  • servicio web WCF

Tengo un certificado de servidor (formato p7b de una CA de confianza) que he instalado en mi WCF Web la estación de trabajo de servicio (dev) está utilizando el complemento de certificado mmc (en este momento cert está en los editores de confianza). No creo que necesite otro certificado en el servidor Java, ya que la respuesta debe ser clara (ni firmada ni encriptada). Todavía estoy un poco confundido con este certificado, y con los certificados en general, ya que parece contener solo una clave pública.

Aquí está la aplicación.config de mi proyecto de prueba:

<client> 
    <endpoint address="http://entrust-user-certification-uat.fm.rbsgrp.net/rbs/WebAS" 
    behaviorConfiguration="endpointCredentialsBehavior" binding="wsHttpBinding" 
    bindingConfiguration="WebAsServicePortTypeBinding" contract="IWebAsServicePortType" 
    name="WebAsServicePortType"> 
    <!--<identity> 
     <dns value="entrust-user-certification-uat.fm.rbsgrp.net" /> 
    </identity>--> 
    </endpoint> 
</client> 
<bindings> 
    <wsHttpBinding> 
    <binding name="WebAsServicePortTypeBinding" closeTimeout="00:01:00" 
     openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 
     bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" 
     maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" 
     textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false"> 
     <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" 
     maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 
     <security mode="Message"> 
     <message clientCredentialType="Certificate" negotiateServiceCredential="false" 
      establishSecurityContext="false" /> 
     </security> 
    </binding> 
    </wsHttpBinding> 
</bindings> 

<behaviors> 
    <endpointBehaviors> 
    <behavior name="endpointCredentialsBehavior"> 
     <clientCredentials> 
     <clientCertificate findValue="entrust-user-certification-uat.fm.rbsgrp.net" 
      storeLocation="LocalMachine" storeName="TrustedPublisher" 
      x509FindType="FindBySubjectName"></clientCertificate> 
     <serviceCertificate> 
      <!-- 
      Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate 
      is in the user's Trusted People store, then it will be trusted without performing a 
      validation of the certificate's issuer chain. This setting is used here for convenience so that the 
      sample can be run without having to have certificates issued by a certificate authority (CA). 
      This setting is less secure than the default, ChainTrust. The security implications of this 
      setting should be carefully considered before using PeerOrChainTrust in production code. 
      --> 
      <authentication certificateValidationMode="None" revocationMode="NoCheck" trustedStoreLocation="LocalMachine"/> 
     </serviceCertificate> 
     </clientCredentials> 
    </behavior> 
    </endpointBehaviors> 
</behaviors> 

Cuando me encontré con una prueba sencilla:
          WebAS entrustService = new WebAS();
          ActivationCodes certCodes = entrustService.createUser ("testNomad");
Tengo el error:
          fallidos: System.Web.Services.Protocols.SoapException: javax.xml.soap.SOAPException: Ningún elemento de la firma que se encuentra en el mensaje de jabón

Cómo podría ¿Forzo el proceso de firma de cada mensaje? Estaba pensando que podría hacerlo a través de la configuración de WCF con bastante facilidad. ¡Cualquier ayuda sería muy apreciada!

+0

Hola Nomadefv, Cómo hacer que, en el IClientMessageInspector/BeforeSendReques inyectar su cadena XML devuelto en la solicitud? AJR –

+1

Lo sentimos. Un poco fuera de las últimas dos semanas. Puede intentar: – Nomadefv

+0

'public object BeforeSendRequest (ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) '{// ... obtener la var diferente de configuración de configuración segura ' // Cargar el certificado del almacén de certificados. 'X509Certificate2 cert = GetCertificateBySubject (certificateSubjectName, certificateStoreName, certificateStoreLocation); '// Firmar la solicitud ' string signedSoapMessage = SignRequest (request.ToString(), cert, signatureId, keyInfoRefId, bodyId); – Nomadefv

Respuesta

14

OK. Después de algunos intentos y errores, aquí está la solución que usa el patrón SignedXml e IClientMessageInspector/BeforeSendRequest. Muchas gracias al Yaron Naveh por sus sugerencias relevantes.

// Sign an XML request and return it 
public static string SignRequest(string request, string SubjectName, string Signature, string keyInfoRefId) 
{ 
    if (string.IsNullOrEmpty(request)) 
     throw new ArgumentNullException("request"); 
    if (string.IsNullOrEmpty(SubjectName)) 
     throw new ArgumentNullException("SubjectName"); 

    // Load the certificate from the certificate store. 
    X509Certificate2 cert = GetCertificateBySubject(SubjectName); 

    // Create a new XML document. 
    XmlDocument doc = new XmlDocument(); 

    // Format the document to ignore white spaces. 
    doc.PreserveWhitespace = false; 

    // Load the passed XML 
    doc.LoadXml(request); 

    // Add the declaration as per Entrust sample provided -don't think it's necessary though 
    if (!(doc.FirstChild is XmlDeclaration)) 
    { 
     XmlDeclaration declaration = doc.CreateXmlDeclaration("1.0", "UTF-8", string.Empty); 
     doc.InsertBefore(declaration, doc.FirstChild); 
    } 

    // Remove the Action (MustUnderstand). 
    // TODO: Need to find a more elegant way to do so 
    XmlNode headerNode = null; 
    XmlNodeList nodeList = doc.GetElementsByTagName("Action"); 
    if (nodeList.Count > 0) 
    { 
     headerNode = nodeList[0].ParentNode; 
     headerNode.RemoveChild(nodeList[0]); 
    } 

    // Set the body id - not in used but could be useful at a later stage of this project 
    XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);  
    ns.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/"); 
    XmlElement body = doc.DocumentElement.SelectSingleNode(@"//s:Body", ns) as XmlElement;  
    if (body == null)  
     throw new ApplicationException("No body tag found"); 
    body.RemoveAllAttributes(); // no need to have namespace 
    body.SetAttribute("Id", "ABC"); // Body Id could be passed as a param 

    // Create a custom SignedXml object so that we could sign the keyinfo 
    CustomSignedXml signedXml = new CustomSignedXml(doc); 

    // Add the key to the SignedXml document. 
    signedXml.SigningKey = cert.PrivateKey; 

    // Create a new KeyInfo object. 
    KeyInfo keyInfo = new KeyInfo(); 
    keyInfo.Id = keyInfoRefId; 

    // Load the certificate into a KeyInfoX509Data object 
    // and add it to the KeyInfo object. 
    KeyInfoX509Data keyInfoData = new KeyInfoX509Data(); 
    keyInfoData.AddCertificate(cert); 
    keyInfo.AddClause(keyInfoData); 

    // Add the KeyInfo object to the SignedXml object. 
    signedXml.KeyInfo = keyInfo; 

    // Create a reference to be signed. 
    Reference reference = new Reference(); 
    reference.Uri = ""; 

    // Add an enveloped transformation to the reference. 
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); 
    reference.AddTransform(env); 

    // Add the reference to the SignedXml object. 
    signedXml.AddReference(reference); 

    Reference reference2 = new Reference(); 
    reference2.Uri = "#" + keyInfoRefId; 
    signedXml.AddReference(reference2); 

    // Add the Signature Id 
    signedXml.Signature.Id = Signature; 

    // Compute the signature. 
    signedXml.ComputeSignature(); 

    // Get the XML representation of the signature and save 
    // it to an XmlElement object. 
    XmlElement xmlDigitalSignature = signedXml.GetXml(); 

    // Append the Signature element to the XML document. 
    if (headerNode != null) 
    {     
     headerNode.AppendChild(doc.ImportNode(xmlDigitalSignature, true)); 
    } 

    return doc.InnerXml; 
} 

public static X509Certificate2 GetCertificateBySubject(string CertificateSubject) 
{ 
    // Check the args. 
    if (string.IsNullOrEmpty(CertificateSubject)) 
     throw new ArgumentNullException("CertificateSubject"); 

    // Load the certificate from the certificate store. 
    X509Certificate2 cert = null; 

    X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); 

    try 
    { 
     // Open the store. 
     store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); 

     // Find the certificate with the specified subject. 
     cert = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateSubject, false)[0]; 

     // Throw an exception of the certificate was not found. 
     if (cert == null) 
     { 
      throw new CryptographicException("The certificate could not be found."); 
     } 
    } 
    finally 
    { 
     // Close the store even if an exception was thrown. 
     store.Close(); 
    } 

    return cert; 
} 

y la clase CustomSignedXml:

public class CustomSignedXml : SignedXml 
{ 
    public CustomSignedXml(XmlDocument doc) : base(doc) 
    { 
     return; 
    } 
    public override XmlElement GetIdElement(XmlDocument doc, string id) 
    { 
     // see if this is the key info being referenced, otherwise fall back to default behavior 
     if (String.Compare(id, this.KeyInfo.Id, StringComparison.OrdinalIgnoreCase) == 0) 
      return this.KeyInfo.GetXml(); 
     else 
      return base.GetIdElement(doc, id); 
    } 
} 
+0

Hola, no tengo muy claro cómo usar esto en el método BeforeSendRequest. ¿Puedes explicar? –

0

¿Puede capturar mensajes enviados por su servicio WCF? Por cierto. es la seguridad de los mensajes utilizada por el servicio Java que se describe en WSDL; esto facilitaría las cosas mucho más.

Según su descripción, creo que su configuración es incorrecta porque cuando se utilizan las credenciales del certificado de cliente necesita dos certificados: certificado de cliente con clave pública y privada y certificado de servidor con clave pública.

es también probable que describen en sus requisitos:

Does the message contain a KeyInfo x.509 certificate

Is the certificate issued from a trusted CA – configuration based

¿Por qué necesitaría para enviar de vuelta certificado de servicio que ya está instalado en el servidor? ¿Por qué el servicio debe verificar si su certificado es de CA confiable? Supongo que estos requisitos dicen que debe crear un nuevo certificado para su cliente.

Pero solo son suposiciones porque los requisitos reales generalmente se describen en lenguaje compartido: aserciones WSDL + WS-Security.

Control La firma y el cifrado son posibles en varios niveles. En primer lugar, cada ServiceContract y MessageContract tiene la propiedad ProtectionLevel que es por defecto EncryptAndSign. Puede cambiarlo a Sign.

+0

¡Muchas gracias por sus comentarios! Después de un día intenso mirando nuevamente este problema y hablando con el desarrollador de Java, realmente deberíamos necesitar solo un certificado en el cliente (wcf) y simplemente firmar todos los mensajes salientes con él. Miré un poco más e implementar IClientMessageInspector podría ser la solución. Todavía estoy excavando ... – Nomadefv

+0

La seguridad del mensaje del servicio Java wev no se describe en WSDL. Mirando ahora IClientMessageInspector y BeforeSendRequest. – Nomadefv

Cuestiones relacionadas