Encrypt WCF message password - c#

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?

Related

creating a client to call a web service without a wsdl in c#

I need to connect to a web service without knowing the wsdl; the only info I have is a sample request like the one below but I have no idea where to start from to build this xml; for example what is wsse:BinarySecurityToken? and do I have to create also prefixes like "wsu" or "wsse" ???
The info below are from a test server so there are any sensitive data disclosed here.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap: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="Username-123" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>gen-emiswebmobile.e-mis.co.uk</wsse:Username>
</wsse:UsernameToken>
<wsu:Timestamp wsu:Id="Timestamp-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Created>2017-06-29T22:42:44.870Z</wsu:Created>
<wsu:Expires>2017-06-29T22:57:44.870Z</wsu:Expires>
</wsu:Timestamp>
<wsse:BinarySecurityToken wsu:Id="CertId-4468839874371617328" 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">MIIFVjCCBD6gAwIBAgIHTvS0LmWypjANBgkqhkiG9w0BAQsFADCBtDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMS0wKwYDVQQLEyRodHRwOi8vY2VydHMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS8xMzAxBgNVBAMTKkdvIERhZGR5IFNlY3VyZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjAeFw0xNDEwMDgwNzMzNDdaFw0xNjEwMDgwNzMzNDdaMEsxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDEmMCQGA1UEAxMdZ2VuLWVtaXN3ZWJtb2JpbGUuZS1taXMuY28udWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzpXaDzByOKYA0C1MCa2fzDzazgiJBqfHh08/kb4cf/8Vceu1mTst1ggVOrvIpTVf+jUmj05OYI1I5gSEUN+mEXOJv/mK5Fmalx+i7lIxRa3xT47KMcbAmmTzr1IY8CQUMGQhW7idDU680R2eO2n5x1q+2icViQb/Ooa23eBWgH+BwxhUqpi2NrvbHC/Xg3A8tkiIeZCsm5eyVbrzQykbFyeAKhX5ZBw+P0EKJB+mvfQ7clmyGbcBwNw3nPWscxOO2w/OYd7bh7VLiRK3Mgz6OYL3fHMc/tDsEXKXL1j+60ZVJXtZNrFykWNFsA14EV+unouf0cUZqi8kDhvG8ae+DAgMBAAGjggHTMIIBzzAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNVHQ8BAf8EBAMCBaAwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nb2RhZGR5LmNvbS9nZGlnMnMxLTg3LmNybDBTBgNVHSAETDBKMEgGC2CGSAGG/W0BBxcBMDkwNwYIKwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS8wdgYIKwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nb2RhZGR5LmNvbS8wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS9nZGlnMi5jcnQwHwYDVR0jBBgwFoAUQMK9J47MNIMwojPX+2yz8LQsgM4wSwYDVR0RBEQwQoIdZ2VuLWVtaXN3ZWJtb2JpbGUuZS1taXMuY28udWuCIXd3dy5nZW4tZW1pc3dlYm1vYmlsZS5lLW1pcy5jby51azAdBgNVHQ4EFgQUfnm7SRUsh4sUkMHC41QY+Q3ysZ0wDQYJKoZIhvcNAQELBQADggEBAK3zIhwhU3N7bxGjRVTFsQDLvJBjrIeJiRHXTtPF8A/muYJdcapUTcz/cl8mu9hXN3po0WJQii2kttBQD7MAjHlIMDDY6iTDrEorqch0OUSc6tJe6KQlNdeE4Cng11/AlcTBNitxE8aNiC8PUsh4P4Se8jDNoa7ESSgv9MXpHUT+Dfx5wsM4CPkUMhdaQiPQPL4QzbwpphX+hD4DTGGkKR34HR91HLoUap/gOS97ZTUmUBtgOSSKGL+kVrs+HKA6+Zbv55ya0bYFHJikN/5R1XUgZX6l3VZJJvBnmFHETd2I6H/1/VKOiSoD4JNZf7cxfDRmi8cLixE6PdSM41ve6qw=</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-1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>WOHqa2p3/ZcVQ2FU36OEU9gfYaI=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>q/ZGfYAacr6Vv/LwfyAwyxXGLVkJ7qXJYkMvrCludwNRaENHNR1LekPwVuMGQ95uoKzk9npEnefHrCD1+JmufKltDtXLIZmhUJNryhOcUHzSoDLjdCAOJ+Ylccf73fhI7uHJr63NlbmV6IVzTrb3RPpfoJDpuDqyrkqS0l4HQI3BkzbDYR3Eo3ce8oUzfZ32xpLTQ/LceI6DsaHMCtimxjhAJDa1NuRDbsosHgxw8MEms1hc5VwR4s2/h6GJEkz5EXegWkm9CBNlpgceaFF7HMUPZqm7PUTHTrsmfN9uEZlp4iaUQrCBPNwsju3dbD1nOxNAxQeGvx5RNL0xvY5emg==</SignatureValue>
<KeyInfo Id="KeyId-4468839874371617327">
<wsse:SecurityTokenReference wsu:Id="STRId-3E04823056058230" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#CertId-4468839874371617328" />
</wsse:SecurityTokenReference>
</KeyInfo>
</Signature>
</wsse:Security>
<Action xmlns="http://www.w3.org/2005/08/addressing">MiG</Action>
<MessageID xmlns="http://www.w3.org/2005/08/addressing">84CF41DA-133A-442D-979B-D76697AF71BF</MessageID>
<To xmlns="http://www.w3.org/2005/08/addressing">https://185.13.72.96/Miggateway</To>
<ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
</soap:Header>
<soap:Body>
<q1:migRequest xmlns:q1="http://www.healthcaregateway.co.uk/mig">
<q1:serviceDefinition>
<q1:name>MIG.DATAEXTRACTIONSERVICE.EhrExtractRequest</q1:name>
<q1:version>0.0.2</q1:version>
</q1:serviceDefinition>
<q1:serviceHeader>
<q1:source xmlns:q1="http://www.healthcaregateway.co.uk/mig">
<q1:identifier>externaldevuser:urn:hgl:addressing:ods:N00001</q1:identifier>
</q1:source>
<q1:target xmlns:q1="http://www.healthcaregateway.co.uk/mig">
<q1:nationalCode>urn:hgl:addressing:ods:A00005</q1:nationalCode>
</q1:target>
</q1:serviceHeader>
<q1:serviceContent>
<q1:ehrExtractRequestv2-0 xmlns:q1="http://www.healthcaregateway.co.uk/mig">
<q1:id>4df5cdb1-4a20-42a5-b604-6033eec5224d</q1:id>
<q1:consent>
<q1:value>Given</q1:value>
</q1:consent>
<q1:provenance>
<q1:creationTime>2013-07-15T14:37:28.7273043Z</q1:creationTime>
<q1:system>
<q1:id>BED045C0-BC58-4934-8E11-2FC08F730060</q1:id>
<q1:name>EMISWebCR1 50002</q1:name>
</q1:system>
</q1:provenance>
<q1:patient>
<q1:primaryIdentifier type="NHS">8888888888</q1:primaryIdentifier>
</q1:patient>
<q1:requestSpecification>
<q1:id>654d8e02-6b09-4754-a095-968c397db560</q1:id>
<q1:responseFormat>OPENHR</q1:responseFormat>
<q1:request>
<q1:name>scm.diagnosis</q1:name>
<q1:includeAssociatedText>true</q1:includeAssociatedText>
</q1:request>
</q1:requestSpecification>
</q1:ehrExtractRequestv2-0>
</q1:serviceContent>
</q1:migRequest>
</soap:Body>
You can look here for some code sample.
The xml you posted is the soap envelope xml and you should be able to send it as shown in the sample (with LoadXml) and get a response. (in the xml you posted you invoke the MiG action with migRequest).
If you dont have the soap envelope request you will have to create one and it is the tricky part if you dont know which functions to call.
The structure of the request is this: (in your sample there is also security which is not present here)
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<MethodFoo>
<param1>value</param1>
<param2>value</param1>
</MethodFoo>
</soap:Body>
</soap:Envelope>
Look here for more on the request message structure.
There are few ways to connect to a web service. Basically, you use one of the class to "post" the exact xml to the URL.
The common ways are to use WebClient, HttpClient. It is a relatively simple to post an xml to an URL.
If I understand you correctly, you are looking at ways of how to construct the Xml in the exact format required.
One way, we have handled a similar request is by the following approach.
Take the sample xml and convert it to a template
for eg,
<q1:system>
<q1:id>BED045C0-BC58-4934-8E11-2FC08F730060</q1:id>
<q1:name>EMISWebCR1 50002</q1:name>
</q1:system>
becomes
<q1:system>
<q1:id>~system_id~</q1:id>
<q1:name>~system_name~</q1:name>
</q1:system>
In the above sample ~system_id~, ~system_name~ are the placeholders. In your application code you do a find and replace with the actual values.
This does look like "low key"... but in the good old days (i.e., before we have xml everywhere), this was a popular approach :-)
Another more Xml way is to construct a C# class in the exact format. You populate your object with relevant values. Then you serialize your C# class to xml and post it to the URL. There are some popular SO links for this:
Generate C# class from XML

WebServicesClientProtocol add EncodingType to Nonce in Security header

Similar question: How do I add an EncodingType attribute to the Nonce element of a UsernameToken in WSE 3.0 (.NET)
I'm trying to modify header that is send by WebServicesClientProtocol to service.
Unfortunately Microsoft's implementation of WSSE Username and Token Security Spec 1.1 isn't compatible with standard and isn't sending EncodingType with Nonce.
In similar question I've linked on top solution was to disable EncodingType validation on server, but I'm not able to modify anything.
I've imported WSDL as Web Reference, I've changed base class to WebServicesClientProtocol
Then inside my code I'm doing this:
var client = new QueryClient();
SoapContext requestContext = client.RequestSoapContext;
requestContext.Security.Timestamp.TtlInSeconds = 60;
var userToken = new UsernameToken(_userName, _password, PasswordOption.SendHashed);
requestContext.Security.Tokens.Add(userToken);
X509SecurityToken signatureToken = GetSecurityToken();
requestContext.Security.Tokens.Add(signatureToken);
MessageSignature sig = new MessageSignature(signatureToken);
requestContext.Security.Elements.Add(sig);
client.SetClientCredential(signatureToken);
client.SetClientCredential(new UsernameToken(_userName, _password, PasswordOption.SendHashed));
this creates request that is almost ideal, but Nonce hasn't got EncodingType:
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-096b3d09-bc08-4d9b-a561-c5c793dd7197">
<wsse:Username>ws_test_user</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">XrFybEBGGqAIp2ybV6BbAdGa01U=</wsse:Password>
<wsse:Nonce>gXsJgA6vV/HwY4pew9pi9Q==</wsse:Nonce>
<wsu:Created>2017-02-03T12:17:57Z</wsu:Created>
</wsse:UsernameToken>
Nonce must have this attribute: EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
How can I add this attribute? I'd like to avoid manually creating request because I must specify Username, Password, BinarySecurityToken and Signature. Microsoft.Web.Services3 is creating all necessary elements for me, one thing missing is that attribute.
EDIT:
This is request I'm trying to create:
<soap:Envelope xmlns:dz="http://dom.query.api.com" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://dz.api.swd.zbp.pl/xsd">
<soap: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:UsernameToken wsu:Id="UsernameToken-E94CEB6F4708FB7C23148611494797612">
<wsse:Username>my_login</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">XqEwZ/CxaBfFvh487TjvN8qD63c=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">JzURe0CxvzRjmEcH/ndldw==</wsse:Nonce>
<wsu:Created>2017-02-09T09:42:27.976Z</wsu:Created>
</wsse:UsernameToken>
<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="X509-E94CEB6F4708FB7C2314861149479517">MIIKnDCCB.........nmIngeg6d6TNI=</wsse:BinarySecurityToken>
<ds:Signature Id="SIG-E94CEB6F4708FB7C23148611494795311" 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="dz soap xsd" 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="#id-E94CEB6F4708FB7C23148611494795310">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="dz xsd" 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>mlABQuNUFOmLqsDswxXxQ6XnjpQ=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>lYhBHSQ/L...XL1HEbMQjJ/Q2Rvg==</ds:SignatureValue>
<ds:KeyInfo Id="KI-E94CEB6F4708FB7C2314861149479518">
<wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="STR-E94CEB6F4708FB7C2314861149479519" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd">
<wsse:Reference URI="#X509-E94CEB6F4708FB7C2314861149479517" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</soap:Header>
<soap:Body wsu:Id="id-E94CEB6F4708FB7C23148611494795310" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<dz:query>
<dz:param>
<xsd:userQueryId>27467</xsd:userQueryId>
</dz:param>
</dz:query>
</soap:Body>
</soap:Envelope>
I've send my request to service creator and he confirm that all I need is that EncodingType attribute in Nonce
The EncodingType flag is according to the WSSE Username and Token Security Spec 1.1, which is the spec required by the version of the Apache CXF framework that this Java Web Service is using. .NET does not meet that spec. Luckily there was a flag in CXF to turn off the requirement. We did that and are now able to communicate.

WCF is adding an envelope header with a "MustUnderstand" Attribute that a third party service I do not control can't handle

I've boiled my problem down to the <Action s:mustUnderstand="1" bit below.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"></Action>
<ActivityId CorrelationId="ec9c7c4e-2e7e-4310-8ad1-99e453b29560" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">45abb8ea-8d9f-4fc2-aeb7-580884cae02e</ActivityId>
</s:Header>
<s:Body>
[redacted]
</s:Body>
</s:Envelope>
When mustUnderstand=1 is there the service barfs, and returns a web server 500 error page, not xml; with it mustUnderstand=0 or the entire <Action removed it works like a dream.
I've tried both standard basicHttpBinding as well as the following customBinding trying to simplify the messages from here:
<binding name="httpSoap12">
<textMessageEncoding messageVersion="Soap12" />
<httpTransport />
</binding>
I've seen lots of people struggling with this particular issue; but nobody seems to have a solution that I can find.
Removal of the Action bit wholesale, or setting mustUnderstand="0" both allow the service to process my incoming message. I'd welcome a solution to do either of those, or an alternative I have yet to come up with. I should specify I've done that removal/modification manually. I cannot find a way to do it from my code.

WCF Client With X.509 Certificate and Java Web Service

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.

Issue with Lumesse Web Services and WSSE plain text security

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.

Categories

Resources