I'm currently trying to develop a client that interacts with a 3rd party web service. The third party web service is written in Java, and we have supplied them with a CA X509 certificate that is used to sign the messages. The 3rd party specifies WS-Security 1.1 (http://docs.oasis-open.org/wss/v1.1/wss-v1.1-spec-os-SOAPMessageSecurity.pdf).
We are able to successfully sign the message, send and get a response through SoapUI, but I am having trouble to get the same functionality to work in WCF (.NET 4.5). I've looked at the message being sent from the WCF client (via SvcTraceViewer and the message logs), and it appears the format is slightly different from that in SoapUI.
When I send the message, I get the following exception:
There was no endpoint listening at https://<service addres> that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
The inner exception is Unable to connect to the remote server, and the inner exception for that is A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond XXX.XXX.XXX.XXX:443.
Since the endpoint is up and available, I'm thinking my binding is not set up entirely correctly.
I've checked questions here on SO, including Signing SOAP messages using X.509 certificate from WCF service to Java webservice and read Yaron Naveh's 12 common wcf interop confusions which have helped, but haven't gotten me all the way there yet.
Binding defintion:
<customBinding>
<binding name="myCustomBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualCertificate"
defaultAlgorithmSuite="Basic128Sha256Rsa15"
includeTimestamp="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
securityHeaderLayout="LaxTimestampLast" />
<httpsTransport />
</binding>
</customBinding>
Method to create the proxy factory:
private Task<ChannelFactory<T>> CreateChannelFactory<T>(string bindingConfig, string address)
{
EndpointAddress endpoint = new EndpointAddress(new Uri(address), EndpointIdentity.CreateDnsIdentity(<identity name>));
ChannelFactory<T> proxy = new ChannelFactory<T>(new CustomBinding(bindingConfig), endpoint);
proxy.Credentials.ClientCertificate.SetCertificate(storeLocation, storeName, findByType, findByValue);
proxy.Credentials.ServiceCertificate.SetDefaultCertificate(storeLocation, storeName, findByType, findByValue);
return Task.FromResult<ChannelFactory<T>>(proxy);
}
The 3rd party actually has several services (one operation contract per service, essentially), so I generate ChannelFactory<T> objects for each service at client start using async/await, and the values storeLocation, storeName, findByType and findByValue for the certificate are stored globally in the client (in case any one was wondering).
When I send the message, I create a channel from the appropriate factory. And then I get the error.
Here is the relevant part of the message sent via SoapUI (that works):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:view="http://<service namespace>">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
wsu:Id="X509-F4F47BCAA968D14D08143033737254925">(data)</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-F4F47BCAA968D14D08143033737254928"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="soapenv view"
xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#TS-F4F47BCAA968D14D08143033737254624">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="wsse soapenv view"
xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>hg5n7PfuAfYb/LEawatI4ZBK0wmy14+Y6DihGhgBMI4=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>(data)</ds:SignatureValue>
<ds:KeyInfo Id="KI-F4F47BCAA968D14D08143033737254926">
<wsse:SecurityTokenReference wsu:Id="STR-F4F47BCAA968D14D08143033737254927">
<wsse:Reference URI="#X509-F4F47BCAA968D14D08143033737254925"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsu:Timestamp wsu:Id="TS-F4F47BCAA968D14D08143033737254624">
<wsu:Created>2015-04-29T19:56:12Z</wsu:Created>
<wsu:Expires>2015-04-29T19:56:17Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<!-- Not relevant. Not signed -->
</soapenv:Body>
</soapenv:Envelope>
Here is the message produced by WCF with the specified bindings:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo8j1TaWVCpRLgEy0S8UuwAcBAAAAAkQP7Rrb00auQ0G7/7Q0C1x7YNOf+kFOt9ioJVgbfFYACQAA</VsDebuggerCausalityData>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>8KAbTZRA1cC60emKdKIiIm3zvv1jPPfVaia3a9l1c3g=</DigestValue>
</Reference>
<Reference URI="#uuid-5cb02cb6-0ae6-486d-ad2b-f9f9107b4576-2">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>zR0k1GizuQekuM9WcSzVGssZowuzj3Dza/WGYmMqjSo=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>(data)</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
URI="#uuid-96e7c3a3-cbe9-409e-ad49-9dcc07ef4360-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
<u:Timestamp u:Id="uuid-5cb02cb6-0ae6-486d-ad2b-f9f9107b4576-2">
<u:Created>2015-04-30T18:00:28.886Z</u:Created>
<u:Expires>2015-04-30T18:05:28.886Z</u:Expires>
</u:Timestamp>
</o:Security>
</s:Header>
<s:Body u:Id="_1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</s:Body>
</s:Envelope>
Here are differences/missing items from the WCF version compared to the SoapUI version:
<BinarySecurityToken> missing EncodingType, ValueType and Id
attributes.
<Signature> missing Id attribute.
'CanonicalizationMethodmissing
<Reference> two elements instead of 1. Not sure what the URI attribute is for (in the WCF version).
<KeyInfo> missing Id attribute.
<SecurityTokenReference> missing Id attribute.
Everything else seems to match up (with the exception of namespace prefixes and where they are assigned). I would prefer to do this via a custom binding, but I'm open to implementing IClientMessageInspector if I need to massage the outgoing request.
I was able to resolve this issue this morning. There were 2 issues at hand.
The first appears to be related to my company's VPN. I was able to send and receive responses yesterday in the office (as opposed to the day I posted the question, when I was on VPN). Last night on the VPN again I was getting the There was no endpoint listening at https://<service addres> that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. exception again, so I deployed the client to one of our dev servers and was able to communicate successfully again.
The second issue was discovered once I was able to send the message. I saw the response in the trace message logs, but I received a new exception:
Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.
The request is signed (by the client), but the response is unsigned. Setting the enableUnsecuredResponse attribute on the custom binding to true resolved this issue.
The final custom binding configuration looks like this:
<customBinding>
<textMessageEncoding messageVersion="1.1" />
<security authenticationMode="MutualCertificate"
defaultAlgorithmSuite="Basic128Sha256Rsa15"
enableUnsecuredResponse="true"
includeTimestamp="true"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
securityHeaderLayout="LaxTimestampLast" />
<httpsTransport />
</customBinding>
No changes in the code for setting the certificates, with the exception of making the name passed into EndpointIdentity.CreateDnsIdentity configurable.
Related
I have fetched a SAML Token from AD FS for the Relying Party Trust I have set up with my local SharePoint server.
The the important part of the response from ADFS is given below.
Using this, I have been able to retrieve an access token from SharePoint by posting a url-encoded-form to http://mylocalsharepoint/_trust/default.aspx and grabbing the set-cookie - essentially emulating the action of the Login UI.
My question is, is there a better endpoint other than http://mylocalsharepoint/_trust/default.aspx (this is what the Login GUI page uses) as this is returning an entire web page but all I really need is the access token (fedAuth cookie) plus it requires a url-encoded-form -it would be great to be able to just use the XML SOAP message or at least XML.
I have found /_vti_bin/authentication.asmx but that seems to only support username and password mode.
I would really appreciate anyone pointing me in the right direction. Thanks very much.
<trust:RequestSecurityTokenResponse>
<trust:Lifetime>
<wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2022-10-19T16:56:36.105Z</wsu:Created>
<wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2022-10-19T17:56:36.105Z</wsu:Expires>
</trust:Lifetime>
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>urn:sharepoint:spsites</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<trust:RequestedSecurityToken>
<saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_3519cbe0-66fb-4bc3-9a40-91ea06cb0ad7" Issuer="http://ms-adfs.intranet/adfs/services/trust" IssueInstant="2022-10-19T16:56:36.230Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<saml:Conditions NotBefore="2022-10-19T16:56:36.105Z" NotOnOrAfter="2022-10-19T17:56:36.105Z">
<saml:AudienceRestrictionCondition>
<saml:Audience>urn:sharepoint:spsites</saml:Audience>
</saml:AudienceRestrictionCondition>
</saml:Conditions>
<saml:AttributeStatement>
<saml:Subject>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Attribute AttributeName="emailaddress" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml:AttributeValue>billbates#microsotofu.com</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
<saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:X509-PKI" AuthenticationInstant="2022-10-19T16:56:35.639Z">
<saml:Subject>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
</saml:SubjectConfirmation>
</saml:Subject>
</saml:AuthenticationStatement>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#_3519cbe0-66fb-4bc3-9a40-91ea06cb0ad7">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>gTz6J3z40UUkqOf1DV3gAe4yel5AD0GVPCJ7xI6ac44=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>ftyI5grqS01/g9zpfUuPn24xXMvJ...</ds:SignatureValue>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIICxDCCAaygAwIBAgIQEqN9pL4STbx...</X509Certificate>
</X509Data>
</KeyInfo>
</ds:Signature>
</saml:Assertion>
</trust:RequestedSecurityToken>
<trust:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</trust:TokenType>
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
<trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
</trust:RequestSecurityTokenResponse>
You need to developing a C# customization as Farm Solution to include a custom endpoint to your SharePoint side like an ASHX prepared to receive the given XML contract above and redirect to this new custom endpoint, the C# code needs the minimal three things:
Guarantee security using CORS approach;
Deserialize the received XML from custom authentication provider;
And, the last one: developing custom Authorization into SharePoint side;
About the item 3, it's recommended this Microsoft Learn topic: Create a claims provider in SharePoint
I have a requirement where I have to call 3rd Party web service using wcf client. The third party service which I have to call is secure web service and uses https so for ex. https://kavyen.com/md. The service provider has provided me both server and client certificates.
I have to create a wcf client which Signs the SOAP Request but doesn't encrypt, so in other words I need to have Signing information in SOAP Header but doesn't want entire body to be encrypted.
Below is the sample of SOAP envelop which I must have to send from wcf client.
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope 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" soapenv:mustUnderstand="1">
<wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="CertId-639F96823FC161A915140921867132422">MIICmDCCAYACEGkpMaS8nUlq58nKTzVZubIwDQYJKoZIhvcNAQEFBQAwRjELMAkGA1UEBhMCSUUxHjAcBgNVBAoTFVJldmVudWUgQ29tbWlzc2lvbmVyczEXMBUGA1UEAxMOREVWUEtJMSBTdWIgQ0EwHhcNMTMwOTI0MDgwNTQxWhcNMTUwOTI0MDgwNTQxWjBTMQswCQYDVQQGEwJJRTEXMBUGA1UEChMOVEVTVCBDT01QQU5ZIDUxEjAQBgNVBAsTCTYxOTEyODc1NzEXMBUGA1UEAxMOVEVTVCBDT01QQU5ZIDUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMiliAb+XYJpkEMkwPAG4IObXlwnMdnQ04jNWrholmjmY/PgiPV4/oe1opScyHxI26sq+u4U+aBKyx2mgRDLqn2rpgVsEsq60mzFwuYBYRHLUS0rrQki13jbvoOvPWlAMgR42iF0V3GKr34Zm6i4lw4vLJzju+Xwup9UJhP8NM1nAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAEOrsjUXMgMUQlnsupvl3e07/SA6r7idXCXZ/cbIi6ZzyAkPZOsUJQ7nUIckUF04jJTNJWfVYzs7+sY1c67G7r1MjypyfVH4JIckDFmF2Q5MmyePTGICm2ZeNm+3sJI8ApfbMNYDfXSSY7UXJVt1jhE+M3d2JGXirG+XrZC29/dcqIYixTkiBfDEP16LUTPcHseA/DKzGP43O92r5VP1oa07HA1nHt7W2CbT4MMqEpurBMC0EWkc/LzK0LKGUomZSDUHTOKisHPG5AKrV71EFJnR46eq+Ro6C781dbztkuHj8HH+8CdFuknq2+B7XCVVJTv1F3PVrCC5x8gsoR79oAo=</wsse:BinarySecurityToken>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-8">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#Id-762175305">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>WAtX3NtBp52Y5beBeL28QtPq6LE=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
OibCc5mrk6noqbukfcxy8Tt/d8+/JlOm9Nmx3nrD1i00HWjqi3v55sbnUowCPGA+fztRcIXhuWYF
GlQyrRxxPLhnvM6vfk9zEZYbS/34dudp9H8gswPh+wsWa0/nowgSoo+eK5I0AbYNqCIHD3EUAfzG
/Br+gMqtRuZyZbhtKbg=
</ds:SignatureValue>
<ds:KeyInfo Id="KeyId-639F96823FC161A915140921867132623">
<wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STRId-639F96823FC161A915140921867132624">
<wsse:Reference URI="#CertId-639F96823FC161A915140921867132422" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</soapenv:Header>
<soapenv:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-762175305">
<TestMessage>This is a test.</TestMessage>
</soapenv:Body>
</soapenv:Envelope>
The service provider
Use this binding:
<customBinding>
<binding name="NewBinding0">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualCertificate" includeTimestamp="false"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<secureConversationBootstrap />
</security>
<httpTransport />
</binding>
</customBinding>
Also decorate your contracts with this attribute:
[System.ServiceModel.ServiceContractAttribute(ConfigurationName=..., ProtectionLevel=System.Net.Security.ProtectionLevel.Sign)]
The contract is in reference.cs.
We're currently re-developing a legacy project that makes use of a SOAP web service from Lumesse, called over HTTPS.
http://developer.lumesse.com/Getting_Started
This is being consumed in a ASP.NET application; the original developer (who has long since left) never took advantage of using the provided WSDL, preferring to manually construct the request and parse the response. Whilst this is utter madness, this is what Lumesse's documentation actually recommends when being consumed from .NET, as their service uses obsolete WSSE plain text security.
Whilst we would not usually go against the grain, we'd much prefer to use the in-built support for consuming SOAP web services as opposed to rolling our own solution as the previous developer has.
We've had a few issues already, such as the failure to generate temporary classes that we have hacked around.
Unfortunately, we are now stuck when it comes to sending a successful SOAP request.
The exception that is thrown when we make a request is:
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
Digging deeper, the actual response is as follows:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header />
<env:Body>
<env:Fault xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">wsse:FailedCheck</faultcode>
<faultstring>Expired message.</faultstring>
</env:Fault>
</env:Body>
</env:Envelope>
Which appears to be that 'the signature or decryption was invalid'. Using the other developer's hobbled together solution, with the same credentials works as expected.
Why would it be failing here and what can we do to fix it?
Is it possible without resorting to rolling our own request and response (or even recommended?) service?
http://schemas.xmlsoap.org/specs/ws-security/ws-security.htm
The WSDL we have used, supplied from Lumesse:
https://api3.lumesse-talenthub.com/CareerPortal/SOAP/FoAdvert?WSDL
The endpoint we are hitting:
https://api3.lumesse-talenthub.com/CareerPortal/SOAP/FoAdvert?api_key=xxx
Here is our code so far, heavily based off With C#, WCF SOAP consumer that uses WSSE plain text authentication? - which looks like the same issue.
Correct way communicate WSSE Usernametoken for SOAP webservice has the same issue, but the answer has us storing the binding details in the web.config.
using (LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient client = new LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient())
{
client.ClientCredentials.UserName.UserName = "xxxx";
client.ClientCredentials.UserName.Password = "xxxx";
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
client.Endpoint.Binding = binding;
var response = client.getAdvertisements(new LumesseSoapTest.FoAdvert.getAdvertisements());
}
Example request Lumesse expects, taken from the part of the previous developers home-rolled solution - works as expected:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.mrted.com/">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-11">
<wsse:Username>xxxx</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
xxxx
</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">5Xhsv3Yp2l1xGpL3pNYy6A==
</wsse:Nonce>
<wsu:Created>2012-06-22T09:07:26.631Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<getAdvertisements xmlns="http://ws.mrted.com/">
<firstResult>0</firstResult>
<maxResults>0</maxResults>
</getAdvertisements>
</soapenv:Body>
</soapenv:Envelope>
Example request we are currently sending:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<ActivityId CorrelationId="d309ce44-ed91-4314-87ee-e3abee4f531e" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">dd9a8c26-e673-464d-87e4-5cb8b76989c3</ActivityId>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2014-09-30T16:15:47.426Z</u:Created>
<u:Expires>2014-09-30T16:20:47.426Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-c3275c63-6d98-4ae3-a7a7-afe314d23d6c-3">
<o:Username>xxxx</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxxx</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<getAdvertisements xmlns="http://ws.mrted.com/">
<firstResult>0</firstResult>
<maxResults>0</maxResults>
</getAdvertisements>
</s:Body>
</s:Envelope>
Any help will be greatly appreciated.
Update
Right, using this hacked together XML, I can get a response from the API.
I just don't have a clue how to generate this from our request. Again any help will be greatly appreciated.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken u:Id="UsernameToken-11">
<o:Username>xxx</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxx
</o:Password>
<o:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
5Xhsv3Yp2l1xGpL3pNYy6A==
</o:Nonce>
<o:Created>2012-06-22T09:07:26.631Z</o:Created>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<getAdvertisements xmlns="http://ws.mrted.com/"><firstResult>0</firstResult><maxResults>10</maxResults>
</getAdvertisements>
</s:Body>
</s:Envelope>
Update 2 (got it working!)
Finally, after about 9 hours we've got somewhere. I'll leave this here in for anybody else who has the utter misfortune of working with Lumesse (or another similar Java web service).
The main issue with the XML we are sending above is the Timestamp node under the Security node. The extraneous Nonce node it'll handle, presumably because it's not the first node under the Security node? Who knows (this is actually my first experience with SOAP / WCF in any form haha!).
So, the Timestamp node needs to go. If you need to use a standard binding like basicHttpBinding
or wsHttpBinding, you'll need to create a custom binding. Here's an example that mimics basicHttpBinding, apparently, taken from http://www.mikeobrien.net/blog/removing-wss-timestamp-from-wcf/
Example config:
<customBinding>
<binding name="MyBinding">
<security authenticationMode="UserNameOverTransport" includeTimestamp="false" />
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport maxReceivedMessageSize="26214400" />
</binding>
</customBinding>
Then just call the service as would, passing in the credentials (you can probably stores these in the web.config alongside the above, but I'm currently damned if I know how).
using (LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient client = new LumesseSoapTest.FoAdvert.FoAdvertWebServiceClient())
{
client.ClientCredentials.UserName.UserName = "xxxx";
client.ClientCredentials.UserName.Password = "xxxx";
foreach (var ad in response.advertisementResult.advertisements)
{
#ad.jobTitle <br />
}
}
Thanks again.
I too am working with the same api and had the same issue. If you scroll down to the bottom of this article: http://www.hanselman.com/blog/BreakingAllTheRulesWithWCF.aspx Scott Hanselman removes the time-stamp through code rather than through the config.
i'm not a WCF expert, and i know just the basics of service security, so maybe most of the things that i'll point out will be wrong.
That said, i need to invoke a 3d party service that requires a specific format for the SOAP header.
They require that the soap header provides:
1) timestamp block
2) Binary Token
3) digest (checksum of a part of data to encrypt)
They've provided me this exemple of request (i've removed the sensible parts)
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"
wsu:Id="...omissis...">
</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-6" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="S" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#TS-5">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="wsse S" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>...omissis...</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
....
...omissis...
....
</ds:SignatureValue>
<ds:KeyInfo Id="KI-...omissis...">
<wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="STR-...omissis..." xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
<wsse:Reference URI="#X509-...omissis..." ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsu:Timestamp wsu:Id="TS-5">
<wsu:Created>2013-03-27T15:10:18.523Z</wsu:Created>
<wsu:Expires>2013-03-27T15:26:58.523Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</S:Header>
<S:Body>
...clear (not encrypted) body of the soap request
</S:Body>
</S:Envelope>
They gave me also a WSDL and an xsd.
What i've done was to create a new web application, using the wsdl as service reference.
Checking the web.config, i can see that this have created a basicHttpBinding like this
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="CPBinding">
<security mode="Transport" />
</binding>
<binding name="CPBinding1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://...omissis"
binding="basicHttpBinding" bindingConfiguration="CPBinding"
contract="BTClient.CPCUVPortType" name="CPCUVPort" />
</client>
</system.serviceModel>
But this binding doesn't use any kind of security policy, so i've created a behaviour that takes into account the certificates (for a mutual certificate) like that
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehaviorForCertificates">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="...omissis..." />
<serviceCertificate>
<scopedCertificates>
<add targetUri="https://...omissis..." storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindByThumbprint" findValue="...omissis..." />
</scopedCertificates>
<authentication certificateValidationMode="PeerTrust" revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
And referenced this on the binding. By inspecting the outgoing messages (using the method BeforeSendRequest of a custom Inspector) i can see that it totally ignores the certificates, sending the same request as the without-behaviour one.
The 3d party service answer to my request like this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsu:Timestamp wsu:Id="Timestamp-..." xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Created>2013-09-06T14:31:28Z</wsu:Created>
<wsu:Expires>2013-09-06T14:36:28Z</wsu:Expires>
</wsu:Timestamp>
<wsse:BinarySecurityToken wsu:Id="SecurityToken-...." EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
...omissis...
</wsse:BinarySecurityToken>
<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="#Timestamp-...">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>...omissis...</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>...omissis...</SignatureValue>
<KeyInfo>
<wsse:SecurityTokenReference xmlns="">
<wsse:Reference URI="#SecurityToken-...omissis..." ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" />
</wsse:SecurityTokenReference>
</KeyInfo>
</Signature>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body wsu:Id="..." xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<SOAP-ENV:Fault>
<faultcode>wsse:FailedCheck</faultcode>
<faultstring>The signature or decryption was invalid</faultstring>
<detail>
<e:myfaultdetails xmlns:e="Some-URI">
<errorCode>500</errorCode>
<message>INTERNAL_SERVER_ERROR</message>
</e:myfaultdetails>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
After doing some research i've read that to provide a BinarySecurityToken as requested i need to implement a customBinding. I've tried different approaches and combinations but i always fail to make progress.
For example, by using this custom behaviour:
<customBinding>
<binding name="cb">
<security authenticationMode="MutualCertificateDuplex" requireDerivedKeys="false" includeTimestamp="true"
messageProtectionOrder="EncryptBeforeSign" messageSecurityVersion="WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10" />
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport />
</binding>
</customBinding>
I receive an Internal server error like this:
ExceptionType: System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.InnerException: System.ServiceModel.FaultException:Internal Error
and, most important...my outgoing request is ALWAYS the same as the basicHttpBinding one!
Obviously i have not well understood something. I can see that, using the basicHttpBindig i can correctly communicate with them, but i fail to provide the required security fragments. If i try to use any other king of binding (for example wsHttpBinding or a customBinding) i receive an error message.
Can someone please help me understanding the correct way to do such job? any help would be very appreciated.
Thanks a lot.
EDIT:
I'm adding the outgoing request:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<ActivityId CorrelationId="dd479557-7e51-41de-822b-d2ac669ff827" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">bbd2f92b-33d5-4ec0-87b6-690f2142cdf5</ActivityId>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-7b22e181-f551-4821-91e0-cf8c9b8d9eef-1">
<u:Created>2013-09-09T12:24:03.563Z</u:Created>
<u:Expires>2013-09-09T12:29:03.563Z</u:Expires>
</u:Timestamp>
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>...omissis...</DigestValue>
</Reference>
<Reference URI="#uuid-7b22e181-f551-4821-91e0-cf8c9b8d9eef-1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>...omissis...=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>...omissis...</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#uuid-e31a3eed-6ac7-4dcb-bfb2-2384764acd93-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>
<s:Body u:Id="_1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CPCUValidityRequest xmlns="http://......omissis...">
<serviceType>3</serviceType>
<arg1>arg1</arg1>
<arg2>arg2</arg2>
...omissis...
</CPCUValidityRequest>
</s:Body>
</s:Envelope>
Solution:
Actually i can't successfully invoke the remote service due to an error (as stated in the comments to the accepted solution. But i can say that this question is answered due to the fact that i've managed to create a request mostly similar to the required one. Many thanks to Yaron.
PS:( An hint for those who will have a similar issue, to check the outgoing/incoming request, try to use the Microsoft Trace Viewer, enabling tracing as suggested in this answer https://stackoverflow.com/a/11678740/2274007 (remember to follow also the advice in the comment))
Please publish your outgoing request.
In your binding I would change to authenticationMode="mutualCertificate". Otherwise it looks good.
Also in order to send just signed message and not encrypted one add this attribute on top of your data contract (reference.cs):
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.SimpleServiceSoap", ProtectionLevel=System.Net.Security.ProtectionLevel.Sign)]
More details on this approach in this wcf security tips article.
Try this customBinding:
<customBinding>
<binding name="cb">
<security authenticationMode="MutualCertificateDuplex"
defaultAlgorithmSuite="Basic128Rsa15"
requireDerivedKeys="false"
enableUnsecuredResponse="true"
messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
</security>
<textMessageEncoding messageVersion="Soap11WSAddressing10"/>
<httpsTransport />
</binding>
</customBinding>
A third party in charge of developing Java based webservice came back to us with requirement that message header needs to look like this:
<soapenv:Header>
<wsse:Security>
<xenc:ReferenceList>
<xenc:DataReference URI="#EncDataId-1"/>
</xenc:ReferenceList>
<wsse:UsernameToken>
<wsse:Username>[snip]</wsse:Username>
<xenc:EncryptedData Id="EncDataId-1" Type="http://www.w3.org/2001/04/xmlenc#Element">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:KeyName>[snip]</ds:KeyName>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>[snip]</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
Given my very surfacish understanding of this security voodoo magic I am having trouble figuring out how configure my client to produce such header. Right now my code looks like this:
client.ClientCredentials.UserName.UserName = "[snip]";
client.ClientCredentials.UserName.Password = "[snip]";
and the header:
<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken u:Id="uuid-e906a1ca-aa63-474c-b4ac-cf9b90ab2435-1">
<o:Username>[snip]</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">[snip]</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
and binding:
<binding name="SMSSoap11">
<security mode="TransportWithMessageCredential" />
</binding>
WCF will not produce this output for you. You will have to write your own token for this and maybe even more. WCF supports only username token with plain password out-of-the-box and your code example even doesn't look like any part of username token specification. If the goal is to use encrypted password with WS-Security then the security header seems incomplete.
You should ask Java developers what are security requirements in terms of WS-SecurityPolicy?