У меня есть уникальное требование, когда мне нужно отправить сильно настроенный мыльный заголовок в запросе внешнему поставщику.Единственный способ, которым мой клиент WCF может взаимодействовать со своей веб-службой, - это использовать комбинацию токена имени пользователя и подписи сообщения во всем конверте (см. Ниже заголовок мыла, предоставленный поставщиком).
<soapenv:Envelope xmlns:bsvc="urn:com.workday/bsvc" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-20" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>Cert509User</wsse:Username>
</wsse:UsernameToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>Lx8YS/gC/oTagK0cn2rzGCQcYSSiZC9CKqIFqd/X8zw=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>p9Z1inN//gcDH85KFfd3RB6jY9hEy93ZqSj1l+sGakpvTgyivTbD0mDXKMpEwQVxCqtsEP9r78voxjlAbgM5PJyMQsmIxz+KQ45LyaA8dDdA4X4TIJ89dgvacT5PY0rtxJD2u2T5cRvQJ7p9etJL4FcQMI9I6XyU7DcKFOuRehE=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIDuzCCAqOgAwIBAgIQK2RKs3P21+p4XAV83a/QLjANBgkqhkiG9w0BAQUFADCBrjETMBEGCgmSJomT8ixkARkWA2NvbTEaMBgGCgmSJomT8ixkARkWCm1hc3RlcmNhcmQxHTAbBgNVBAoTFE1hc3RlckNhcmQgV29ybGRXaWRlMSQwIgYDVQQLExtHbG9iYWwgSW5mb3JtYXRpb24gU2VjdXJpdHkxNjA0BgNVBAMTLUlURiBNQyBQcm9kdWN0aW9uIE5ldHdvcmsgQXBwbGljYXRpb25zIHN1YiBDQTAeFw0xMTA4MDQwOTQwNDlaFw0xNTA4MDMwOTA2NDRaMIGoMQswCQYDVQQGEwJVUzERMA8GA1UECBMITWlzc291cmkxFDASBgNVBAcTC1NhaW50IExvdWlzMTQwMgYDVQQKEytNYXN0ZXJDYXJkIFdvcmxkV2lkZSAtIENvbW1vbiBQcm9kSW5mcmEgU1NMMREwDwYDVQQLEwhzaWduaW5nMTEnMCUGA1UEAxMec3RhZ2Uud29ya2RheUhSLm1hc3RlcmNhcmQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCt4MlJCVNcmXiQIg8pxR4JsR0QpIuBCPadIAo849CRLpZglIKRWrTlxRIBC2YQeW3OkuDEdqYU6wJzn9m6GHTbmOSAy21aVR0eOqQLHltXytdzOJG92HW1IlBVuzwmMKwzEUjhVatLRQjKvTs6TjJ7egfzO8H2yolU59fq/zLcpQIDAQABo10wWzAfBgNVHSMEGDAWgBQCt+lVDTcnQt+zKa7QBi4/hEiVUzAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDAdBgNVHQ4EFgQUM23TyPCInFlw2PnukzGOn8kKldcwDQYJKoZIhvcNAQEFBQADggEBAJeAcKk3YWN12frCQSuKzO4qTBNo+QjUjXEHfYuUl8i2pJHs6tDuDkX36RYPWyXLyMPXHSOoomlVmsCprGLqfTGBf1jW/e7Re3sg3/k1iJFg3f1mMKxGP0MuUvuofc/Nj+ezvvl/Nswn3bsAMgvktM+OR5KEhi293qlix87mpvmuvDUw1ZfoQpgN8AvdiQiRWBN2SXahwzGJo+gRjy6EUGdNgc+lsPDkkKxF6csWsb59yip4t7nTbSjqi5XCjZYfMAG5cDhDELtqge5i1W+1a0mP12xKb5P205HSjH9jF/N67CwOBxuuUXaexsqbLaRfL0Dxo0oFwusnIQ1A2qMgg1c=</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</wsse:Security>
</soapenv:Header>
<soapenv:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
SOAP BODY goes HERE
</soapenv:Body>
</soapenv:Envelope>
Я пробовал комбинацию wshttpbinding,пользовательское связывание с различными поведениями в «app.config».Мне не удалось реплицировать заголовок SOAP, показанный выше, и я не смог подключиться к веб-сервису.Это просто способ установки заголовка, который я не могу настроить через app.config.Поэтому я спросил своего поставщика о том, как я могу скопировать заголовок в клиенте WCF.Они прислали мне блок кода (x509 Authentication.cs), который они протестировали, и подтвердили, что он работает (понятия не имею, как это работает для них).По сути, мне «как-то» нужно перехватить запрос, так как мой клиент WCF отправляет запрос поставщику, после перехвата он каким-то образом передает тело SOAP в качестве входных данных для метода (CreateX509SoapEnvelope («тело SOAP»)). Я прикрепил полный кодx509 Authentication.cs ниже
class x509_Authentication
{
public string CreateX509SoapEnvelope(string xml)
{
string soapXML;
soapXML = "<soapenv:Envelope xmlns:bsvc=\"urn:com.workday/bsvc\"
xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">";
soapXML += "<soapenv:Header>\n";
// Add security block for X.509 certificate
soapXML = "<wsse:Security xmlns:wsse=\"http://docs.oasis-
open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">";
soapXML += "<wsse:UsernameToken wsu:Id=\"UsernameToken-20\"
xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-
wss-wssecurity-utility-1.0.xsd\">";
soapXML += "<wsse:Username>Cert509User</wsse:Username>";
soapXML += "</wsse:UsernameToken>";
soapXML += "</wsse:Security>";
soapXML += "</soapenv:Header>" + xml + "</soapenv:Envelope>";
// Sign Envelope
soapXML = CreateSignatureBlock(soapXML, "wsse:Security");
// Verify that the XML was signed properly
VerifySignedXml(soapXML);
return soapXML;
}
public string CreateSignatureBlock(string xml, string sParentSignatureTagName)
{
try
{
string certificatePath="C:\\Users\\user3434\\Desktop\\certfolder\\cert.p12";
//load xml into a dom
XmlDocument xd = new XmlDocument();
xd.LoadXml(xml);
// Set Certificate
System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new X509Certificate2(certificatePath, "changeit");
//System.Security.Cryptography.X509Certificates.X509Certificate2 cert = x509_Authentication.GetCertificateFromStore();
SignedXml signedXml = new SignedXml(xd);
signedXml.SigningKey = cert.PrivateKey;
// Create a new KeyInfo object.
KeyInfo keyInfo = new KeyInfo();
keyInfo.Id = "";
// 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;
// Need to use External Canonicalization method.
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";
// 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);
reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
// Add the Signature Id
signedXml.Signature.Id = "";
// 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. It will find the element after which we want to insert the signature
XmlNodeList nodeList = xd.GetElementsByTagName(sParentSignatureTagName);
if (nodeList.Count > 0)
{
XmlNode headerNode = nodeList[0];
headerNode.AppendChild(xd.ImportNode(xmlDigitalSignature, true));
}
return xd.InnerXml;
}
catch
{
return xml;
}
}
public void VerifySignedXml(String xml)
{
// Create a new XML document.
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(xml);
// Create a new SignedXml object and pass it
// the XML document class.
SignedXml signedXml = new SignedXml(xmlDocument);
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
Reference reference = new Reference();
reference.AddTransform(env);
signedXml.AddReference(reference);
// Find the "Signature" node and create a new XmlNodeList object.
XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature");
// Load the signature node.
signedXml.LoadXml((XmlElement)nodeList[0]);
// Check the signature and return the result.
//if (!signedXml.CheckSignature(cert,true))
//if (!signedXml.CheckSignature(new X509Certificate2(certificatePath, "sdfdf"), true))
//{
// log.Error("Invalid Signature");
//}
}
}
}
Код принимает мыльное тело и соединяет пользовательский заголовок и подписывает весь возвращаемый заголовок конверта в виде строки.Мне нужно взять эту строку, передать ее обратно в запрос и отправить по пути к поставщику.Это звучит слишком сложно для меня теоретически.Но в ходе исследований я обнаружил, что существует способ перехватить исходящее сообщение, реализовав интерфейс IClientMessageInspector и переопределив метод «BeforeSendRequest».Я получил работающую часть кода, где при запуске клиента WCF вызывается метод BeforeSendRequest.Но теперь я застрял в том, как извлечь тело SOAP из исходящего сообщения (я вижу тело при отладке) и отправить его в качестве входных данных для метода Createx509Envelope, а затем взять выходные данные метода и поместить его обратно в «запрос »и отправьте сообщение поставщику. См. мою реализацию метода BeforeSendRequest (я ничего не застрял там)
public string RequestMessage { get; set; }
public string ResponseMessage { get; set; }
object IClientMessageInspector.BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
{
**HOW TO EXTRACT SOAPBODY FROM “.ServiceModel.Channels.Message request” OBJECT and CONVERT THAT TO STRING ??????????**
x509_Authentication x509 = new x509_Authentication();
this.ResponseMessage = x509.CreateX509SoapEnvelope(SOAP Body);
**TAKE THE RESPONSE MESSAGE AND CONVERT BACK TO “.ServiceModel.Channels.Message request” AND SEND THE REQUEST ALONG???????????**
return null;
}
Если есть лучший способ его реализации?пожалуйста, предоставьте образцы .. это первый случай, когда мне пришлось отправлять пользовательский заголовок SOAP поставщику, и это сложно для меня.Не хватает времени.ПОЖАЛУЙСТА ПОМОГИ!!!!!!