Assinando mensagens SOAP usando o certificado X.509 do serviço WCF para o serviço da Web Java

É a minha primeira pergunta na web. Espero que faça sentido.

Já vi diversos blogs relacionados a esse assunto na Web e tentei algumas das idéias apresentadas sem sucesso. Aqui está minha situação:

Eu tenho um aplicativo da web chamando um serviço da web do WCF que, em seguida, chama um serviço da web Java. Eles estão todos em servidores diferentes. A chamada entre o serviço da web do WCF para o serviço da web java não está acima de https, pois o certificado será suficiente para identificar o responsável pela chamada (Segurança da mensagem, portanto).

  • Serviço da Web Java (checkbox preta)

O serviço da Web Java requer uma mensagem assinada e funciona conforme abaixo:
Antes de cada solicitação ser processada, um manipulador intercepta todas as mensagens recebidas e executa as seguintes regras de validação:
1. A mensagem contém um header de segurança
2. A mensagem contém o ID do header de segurança correto?
3. A mensagem foi assinada corretamente?
4. A mensagem contém um certificado KeyInfo x.509
5. O certificado é emitido de uma configuração confiável da CA?
6. O certificado é válido (não expirado, revogado)
7. O certificado contém o OID correto da política

Depois que todas essas etapas forem confirmadas, a mensagem poderá ser processada. Se alguma etapa falhar, será exibida uma exceção de mensagem de soap.

O header de segurança SOAP deve ser validado em relação à especificação de assinatura digital xxx … w3.org/TR/SOAP-dsig/.

A descrição mais completa pode ser encontrada aqui xxx … ibm.com/developerworks/webservices/library/ws-security.html Este artigo da IBM lista os detalhes de cada header do WS-Security, além disso, uma amostra de mensagem SOAP assinada foi fornecida.

Ao assinar a mensagem SOAP, você também deve adicionar o certificado x.509 na mensagem KeyInfo. Isso é necessário para a validação do certificado.

A solicitação SOAP deve gostar disso:

            soe1PnaGXVGrsauC61JSHD+uqGw=    Y9SRPQ9TcDu+GazO3LFwodEdhaA=   jBX/8XkY2aCte7qgXEp1sbNWmQcK/90iVL58sAvwYAEcBABGzOk2agxR0HvWrNa6ixkocAQ205lggwOxnxZJvoVozVYAAjcLtayPBOUYrnSEBFrwKWP/vxgvUDRIdXeIuw5GLY87NrTQMm1Ehf/HvMX9hTBJn4Nm8RdDiUmPcIo=   MIIEbZCCA1WgAwIBAgIES1XpMjANBgkqhkiG9w0BAQUFADBYMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTAeFw0xMDA0MjIxMDQ4MDBaFw0xMzA0MjIxMTE4MDBaMGoxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDEXMBUGCgmSJomT8ixkARkWB2VudHJ1c3QxEjAQBgNVBAMTCWVudHJ1c3RTTTESMBAGA1UEAxMJZW50cnVzdENBMRAwDgYDVQQDEwdSYnMgUmJzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMf88L2JjLPG1hNmTA/KBiC53WVwS2WU9Jh3lC1Rob6RMzOojomZ/dNrvSRB6nzWeXJpZXwik4XFrsAq24By2SZpLTO4p8Vcq71mTAfDu33cnO49Au2pwNvcMn5qIKBk1Xx+oVb4fzK9ncTRu7bW46HsIYth+qkGhbI2JEHwr/zwIDAQABo4IBrzCCAaswCwYDVR0PBAQDAgeAMCsGA1UdEAQkMCKADzIwMTAwNDIyMTA0ODAwWoEPMjAxMjA1MjgxNTE4MDBaMCMGA1UdIAQcMBowCwYJYIZIAYb6awoEMAsGCSqGSIb2fQdLAzAbBgNVHQkEFDASMBAGCSqGSIb2fQdEHTEDAgEBMIHGBgNVHR8Egb4wgbswb6BtoGukaTBnMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTENMAsGA1UEAxMEQ1JMMTBIoEagRIZCZmlsZTovLy8vTVNJREhVLTQ0NUE0RkVFL0NSTC9lbnRydXN0Y2FfZW50cnVzdHNtX2xvY2FsX2NybGZpbGUuY3JsMB8GA1UdIwQYMBaAFBvSL6cPz8L5shubV58yf0pczKzuMB0GA1UdDgQWBBT1/j6OSS8FTjwqluvew16sv7h+VzAJBgNVHRMEAjAAMBkGCSqGSIb2fQdBAAQMMAobBFY4LjADAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQBXxRIA4HUvGSw4L+4uaR51pY4ISjUQWo2Fh7FYBMt29NsKCTdur1OWVVdndt1yjXP4yWXxoAhHtvZL+XNALUFlR2HAWiXuL1nRcxHkB98N5gPqQzW/lJk9cLtL4hVp28EiEpgmKT3I3NP2Pdb2G5MMOdvQ/GFb2y6OwblR8ViPQ8B2aHWzXMrH+0qadPAuBhXyAohwb+mMuYT/ms6xpGi1NMYuYMf6XONz9GkZgnGnMwa+9CCQws1HNz8WYHtmFIxLsVuEWc/0a1vg4IYX1Ds/ttyhJGTVXOSJSkBz8kRyj1pNBDdc1KeG8M++O8m8VgRTJvYaPc7NMiclISukGpea       0061020051    
  • Serviço da Web do WCF

Eu tenho um certificado de servidor (formato p7b de uma CA confiável) que eu instalei onde minha estação de trabalho de serviço da Web WCF (dev) está usando o snap-in de certificado mmc (no momento em que cert está nos editores confiáveis). Eu não acho que eu preciso de outro certificado no servidor Java como a resposta deve estar clara (nem assinado ou criptografado). Eu ainda estou um pouco confuso sobre este certificado e certificados em geral, como parece que detém apenas uma chave pública.

Aqui está o app.config do meu projeto de teste:

   <!--  -->                          

Quando eu corri um teste simples:
WebAS entrustService = novo WebAS ();
ActivationCodes certCodes = entrustService.createUser (“testNomad”);
Eu tenho o erro:
failed: System.Web.Services.Protocols.SoapException: javax.xml.soap.SOAPException: nenhum elemento de assinatura encontrado na mensagem de soap

Como eu poderia forçar o processo de assinatura de cada mensagem? Eu estava pensando que poderia fazê-lo através da configuração do WCF com bastante facilidade. Qualquer ajuda seria muito apreciada !

ESTÁ BEM. Após algumas tentativas e erros, aqui está a solução usando o padrão SignedXml e IClientMessageInspector / BeforeSendRequest. Muito obrigado a Yaron Naveh por suas sugestões 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; } 

e a class 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); } } 

Você pode capturar mensagens enviadas pelo seu serviço WCF? Btw. é a segurança de mensagem usada pelo serviço Java descrito no WSDL – isso tornaria as coisas muito mais fáceis.

Pela sua descrição, penso que a sua configuração está errada porque, quando são utilizadas credenciais de cliente de certificado, são necessários dois certificados – certificado de cliente com chave pública e privada e certificado de servidor com chave pública.

Também é provavelmente descrito em seus requisitos:

A mensagem contém um certificado KeyInfo x.509

O certificado é emitido de uma configuração confiável da CA?

Por que você precisaria enviar um certificado de serviço que já está instalado nesse servidor? Por que o serviço deve verificar se o certificado é de CA confiável? Eu acho que esses requisitos dizem que você deve criar um novo certificado para o seu cliente.

Mas essas são apenas suposições porque os requisitos reais geralmente são descritos em linguagem compartilhada – WSDL + WS-Security assertions.

Controlando Assinatura e criptografia são possíveis em vários níveis. Em primeiro lugar, cada ServiceContract e MessageContract tem a propriedade ProtectionLevel, que é por padrão EncryptAndSign . Você pode alterá-lo para Sign .