With C#, WCF SOAP consumer that uses WSSE plain text authentication? - c#

I have a WCF SOAP consumer that is implemented by Visual Studio 2012 from a WSDL. The WSDL was generated by PeopleTools. The base object is of type System.ServiceModel.ClientBase.
I need the SOAP request to resemble:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://xmlns.oracle.com/Enterprise/Tools/schemas">
<soapenv:Header>
<wsse:Security soap:mustUnderstand="1" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>[plain text username goes here]</wsse:Username>
<wsse:Password>[plain text password goes here]</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<sch:InputParameters>
<Last_Name>Aren</Last_Name>
<First_Name>Cambre</First_Name>
</sch:InputParameters>
</soapenv:Body>
</soapenv:Envelope>
Here's the closest we can get:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:3cc3f2ca-c647-466c-b38b-f2423462c837</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://[internal URL to soap listener]</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityToken Context="uuid-7db82975-2b22-4236-94a1-b3344a0bf04d-1" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeySize>256</t:KeySize>
<t:BinaryExchange ValueType=" http://schemas.xmlsoap.org/ws/2005/02/trust/tlsnego" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">FgMBAFoBAABWAwFQ9IhUFGUO6tCH+0baQ0n/3us//MMXzQA78Udm4xFj5gAAGAAvADUABQAKwBPAFMAJwAoAMgA4ABMABAEAABX/AQABAAAKAAYABAAXABgACwACAQA=</t:BinaryExchange>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
You'll notice two problems:
No plaintext WSSE credentials. Passes a binary form of the credentials that the service won't use.
Authentication is in Body, not Header.
The request omits InputParameters.
Here's the essential C# code:
var service = new ServiceWithBizarreNameFromPeoplesoft();
if (service.ClientCredentials == null)
throw new NullReferenceException();
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential) {Security = new WSHttpSecurity()};
service.Endpoint.Binding = binding;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);
There's no HTTP layer security, and transport is SSL-encrypted. Authentication is only based on the SOAP message.

That is request for WS-SecureConversation token. It is used by WSHttpSecurity by default unless you change its EstablishSecurityContext property to false. Use this binding instead:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
It will use SOAP 1.1 with UserName token and it will require HTTPS transport.
Edit:
For testing without HTTPS try to use this custom binding:
var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.AllowInsecureTransport = true;
var encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transportElement = new HttpTransportBindingElement();
var binding = new CustomBinding(securityElement, encodingElement, transportElement);

This looks to me like wsHttpBindings with Transport security using basic username password authentication.
These lines look wrong to me:
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Mode = SecurityMode.Message;
Here's how I would expect to see this configured in your app or web.config
<bindings>
<wsHttpBinding>
<binding name="ws" >
<security mode="Transport">
<transport clientCredentialType="Basic" proxyCredentialType="Basic" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://www.bla.com/webservice" binding="basicHttpBinding" contract="bla.IService" name="ws" />
</client>
Then the code would look like this:
var service = new GeneratedProxyClient("basic");
service.ClientCredentials.UserName.UserName = "test";
service.ClientCredentials.UserName.Password = "password";
var input = new InputParameters { Last_Name = "Cambre", First_Name = "Aren" };
var returnData = service.BizarrePeopleSoftNameForMethod(input);
Might be better explained here --> http://msdn.microsoft.com/en-us/library/ms733775.aspx

Related

Setting up WSSE for WCF service

I´m looking for a way to add the following WSSE XML data into a SOAP request to a service:
<soapenv: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-D67150EFEFE71BA23416294396650191">
<wsse:Username>XXXXXXX</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXX</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">XXXXXXX</wsse:Nonce>
<wsu:Created>2021-08-20T06:07:45.015Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
I´ve added the service as a service reference in VS 2017 and I´ve seen suggestion that points to app.config like
<bindings>
<basicHttpBinding>
<binding name="myBinding">
<security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://...."
binding="basicHttpBinding" bindingConfiguration="myBinding"
contract="..." name="..." >
<headers>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>XXX</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>XXX</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</headers>
</endpoint>
</client>
And suggestion using BasicHttpBinding like
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
var endpoint = new EndpointAddress("https://....");
using (var client = new ServiceReference1.Client(binding, endpoint))
{
client.ClientCredentials.UserName.UserName = "XYZ";
client.ClientCredentials.UserName.Password = "XYZ";
ServiceReference1.Request request = new ServiceReference1.Request();
request.Request = new ServiceReference1.RequestType();
request.Request.Nr = "12345";
ServiceReference1.Response response = client.getData(request);
ServiceReference1.ResponseType[] responseMessages = response.Response;
}
But I´ve been unable to get anything working. The username and password have been tested via SoapUI.
Any suggestions?
Regards
Kaare

Why web services call only succeeds when pre logined through browser atleast once a day on server

I have following bindings on server side,
<bindings>
<basicHttpBinding>
<binding name="my_BasicHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
and client code running in console on same server,
System.Net.NetworkCredential creds = new NetworkCredential("UserName ", "password", "domain");
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; //.Ntlm; // Basic;
EndpointAddress endpoint = new EndpointAddress("https://example.portal.com + "/_vti_bin/MyServiceFolder/MyService.svc");
ChannelFactory<MyType> factory = new ChannelFactory<MyType>(binding, endpoint);
factory.Credentials.Windows.ClientCredential = creds;
factory.Credentials.UserName.UserName = creds.UserName;
factory.Credentials.UserName.Password = creds.Password;
//factory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
//factory.Credentials.Windows.AllowNtlm = true;
MyType proxy = factory.CreateChannel();
string result = proxy.MyMethod();
Now it usually throws 401 but when I login through browser using same username to view .svc file and try this code again... IT WORKS. BUT again next morning it won't work till I repeat the steps.
Some more details can be found here but with different settings and scenario -
How to make these web services work, man in middle is KEMP too
Could it be related to SharePoint or authentication protocols or the way Kerborse works etc.. got no clue ?

Connect to SSL SOAP Host via "Service Reference" and pass Security Header

I am trying to connect to a SSL SOAP service host by C# using Service Reference.
This is my request message:
<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">uIDPo/zwMmtdsVhFsAVDkQbiV/4AAAAA1zXtnc72UEm+4tlKzvCxsvN6OC2prvRIljIX4XzHKEYACQAA</VsDebuggerCausalityData>
<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>2016-03-18T12:45:27.558Z</u:Created>
<u:Expires>2016-03-18T12:50:27.558Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-2c7986ba-eee5-4411-90a9-a02b625c55ff-1">
<o:Username>MyUserName</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">MyPlainPassword</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">
<generateId xmlns="http://com.vedaadvantage/dp3/Enterprise/StandardTradeCreditCommercial/IndividualCommercialService"/>
</s:Body>
</s:Envelope>
This is the message that my service sends to the host. But the host returns as below:
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.
This is my config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="myBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="UserNameOverTransport"
messageSecurityVersion="WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10" >
</security>
<httpsTransport />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://{URL}"
binding="customBinding"
bindingConfiguration="myBinding"
contract="ServiceReference2.MyService"
name="IndividualCommercialService" />
</client>
</system.serviceModel>
</configuration>
Although when I send the same XML via SOAPUI or other HTTP Post methods it works fine.
I also extract and attached the certificate and user/pass as below:
private static X509Certificate2 DownloadSslCertificate(string strDNSEntry)
{
X509Certificate2 cert = null;
using (TcpClient client = new TcpClient())
{
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
client.Connect(strDNSEntry, 443);
SslStream ssl = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
ssl.AuthenticateAsClient(strDNSEntry);
}
catch (AuthenticationException e)
{
//log.Debug(e.Message);
ssl.Close();
client.Close();
return cert;
}
catch (Exception e)
{
//log.Debug(e.Message);
ssl.Close();
client.Close();
return cert;
}
cert = new X509Certificate2(ssl.RemoteCertificate);
ssl.Close();
client.Close();
return cert;
}
}
private static void Main(string[] args){
var proxy = new MyService();
var uri = proxy.Endpoint.Address.Uri;
var cer = DownloadSslCertificate(uri.DnsSafeHost);
EndpointIdentity identity = EndpointIdentity.CreateDnsIdentity(cer.Subject.Replace("CN=", ""));
EndpointAddress address = new EndpointAddress(proxy.Endpoint.Address.Uri, identity);
proxy.Endpoint.Address = address;
proxy.ClientCredentials.UserName.UserName = "MyUserName";
proxy.ClientCredentials.UserName.Password = "MyPlainPassword";
proxy.ClientCredentials.ServiceCertificate.DefaultCertificate = cer;
proxy.HellowWorld();
}
I am not sure whether the method that I am getting the certificate is correct or not and also why HTTP Post works but my Service Reference Call does not.
Thanks in advance for your help.
Cheers
Try to look inside WSDL (Service References) in order to see hidden files first select Show All Files in Solution explorer.
You`ll se inside service reference Reference.svcmap -> Reference.cs, and inside of this file add ProtectionLevel = System.Net.Security.ProtectionLevel.Sign
as shown below
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://www.your.url/Service/", ConfigurationName = "Service.Service", ProtectionLevel = System.Net.Security.ProtectionLevel.Sign)]
that should help you. Usually it`s really bad idea to modify autogenerated proxy, but seems like that is the only option.

WCF Consumer not attaching client credentials to header

I have a WCF service that uses username authentication, I have a console app that consumes the service and attempts to access a protected method. I run the code and Fiddler says in the auth tab:
No Proxy-Authorization Header is present.
No Authorization Header is present.
Here is my accessing code:
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default;
binding.Security.Transport.Realm = "MyRealm";
ServiceReference1.MobileAPIClient serviceProxy = new ServiceReference1.MobileAPIClient(binding, new EndpointAddress("https://xx.xx.xx.xx/InventoryServices.MobileApi.svc"));
serviceProxy.ClientCredentials.UserName.UserName = "test";
serviceProxy.ClientCredentials.UserName.Password = "test123";
serviceProxy.ChannelFactory.Credentials.UserName.UserName = "test";
serviceProxy.ChannelFactory.Credentials.UserName.Password = "test123";
try
{
serviceProxy.Test();
}
catch (Exception ex)
{
var ex2 = ex;
}
Why are the credentials not being attached to the header?
There is handshaking mode in wcf. So client and service exchange with credentials before first request and then they use only session token.
To disable this mode you should set in web.config
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" negotiateServiceCredential="false" establishSecurityContext="false"/>
</security>

Problem with authentication on java ws

I have application in C# that consume Java WS. Everything worked fine until WS was configured to use authentication. Now I should user login i password to execute methods from WS but I'm not sure how to do it.
I've try
var client = new MyBeanClient();
client.ClientCredentials.UserName.UserName = "admin";
client.ClientCredentials.UserName.Password = "";
client.addConsumer("whatever", "", "", "");
But I get
SecurityMessageException-{"The HTTP request is unauthorized with client
authentication scheme 'Anonymous'. The authentication header received from
the server was 'Negotiate,NTLM'."}
InnerException - (WebException) - {"The remote server returned an error:
(401) Unauthorized."}.
What's wrong?
Thanks
Try this:
var credentialCache = new CredentialCache();
var credentials = new NetworkCredential("username", "password", "domain");
credentialCache.Add(new Uri(client.Url), "NTLM", credentials);
client.Credentials = credentialCache;
client.addConsumer("whatever", "", "", "");
UPDATE:
Sorry in my first post I thought you were using wsdl.exe to generate the client proxy. For a WCF client you need to configure the endpoint:
var basicHttpBinding = new BasicHttpBinding();
basicHttpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
var endpoint = new EndpointAddress("http://example.com/myWindowsAuthN");
var client = new MyBeanClient(basicHttpBinding, endpoint);
client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
client.ChannelFactory.Credentials.Windows.ClientCredential.Domain = "domain";
client.ChannelFactory.Credentials.Windows.ClientCredential.UserName = "username";
client.ChannelFactory.Credentials.Windows.ClientCredential.Password = "password";
UPDATE2:
I've used the following configuration to invoke web services protected with NTLM authentication. In app.config of the client put the following:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="NtlmBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://example.com/SomeWindowsAuthenticatedService"
binding="basicHttpBinding"
bindingConfiguration="NtlmBinding"
contract="IOperationContractOfTheService"
name="WSTestSoap" />
</client>
</system.serviceModel>
and then you could set the corresponding credentials before invoking the method:
using (var client = new MyBeanClient())
{
client.ChannelFactory.Credentials.Windows.ClientCredential =
new NetworkCredential("username", "password", "DOMAIN");
client.addConsumer("whatever", "", "", "");
}

Categories

Resources