Basic Authentication appears to have no security header - c#

I have written a very simple WFCSerice that returns the Windows username supplied. Here is the client side code:
public Form1()
{
ServiceReference1.Service1Client s1 = new ServiceReference1.Service1Client();
s1.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
string str = s1.ReturnWindowsUsername();
InitializeComponent();
}
I can see the credentials in the HTTP Header using Fidddler:
I have tried to do the same thing with Basic Authentication (accessing another web service that supports Basic Authentication). Here is the client side code:
public Form1()
{
InitializeComponent();
ServiceReference1.Service1Client s1 = new ServiceReference1.Service1Client();
s1.ClientCredentials.UserName.UserName = "testuser";
s1.ClientCredentials.UserName.Password = "testpassword";
string str = s1.GetData(1);
}
Here is the screenshot from Fiddler when using Basic Authentication:
Why is there nothing in the header when using Basic Authentication. The Basic Authentication service seems to work as expected. Here is the response (interestingly there appear to be two requests and two responses):

Basic authentication works on the HTTP level. The general flow is that the client requests a resource, then the server issues a challenge, then the client issues a new request with an Authorization header included. If the username and password in the Authorization header are accepted by the server, the client will usually then add the header for subsequent request without going through the request - challenge - re-request-with-authorization steps again.
If you have everything setup correctly, you should expect to see two requests in Fiddler.
One request with no Authorization header included. The response from the server for this request will be a 401 with a WWW-Authenticate: Basic realm="your realm" header attached.
Then you should see a second request where an Authorization header has been sent from the client.
Here is a sample from my environment:
If you don't see the 401 challenge from the server, then basic authentication is not correctly set up.
In order for the service proxy to supply the header, you need to configure your client binding to use <transport clientCredentialType="Basic"/>. Or that's what I did, who knows with WCF with it's myriad of configuration options.
EDIT: I used this on the service side:
<bindings>
<basicHttpBinding>
<binding name="httpTransportCredentialOnlyBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
On the client:
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:53156/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="WcfTest_CBT.IService1"
name="BasicHttpBinding_IService1" />
</client>
I use basicHttpBinding, TransportCredentialOnly and Basic in order to test this easily without SSL hassle etc.

Related

HttpClient get Timeout exceptions from WCF service when request size increase

I am trying to communicate with a WCF SOAP service from my .net6 apllication using HttpClient (I made sure the content wrap up inside an envelope and I set the header content type to be application/soap+xml):
var client =_httpClientFactory.CreateClient();
var response = await client.PostAsync(_uri, new StringContent(message.Body, Encoding.UTF8, mediaType: "application/soap+xml"));
also the server has a certificate configure like that:
<wsHttpBinding>
<binding name="MyHttpsBinding" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" sendTimeout="00:03:20">
<security mode="Transport" >
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
Evrerything works fine but as soon as the request body size increase (just 37k though) I am starting to get timeout exceptions on the client, I also have another client that use the ClientBase class and there everything works.
If I am turning off the certificate mention above I don't get any timeout exception also.

Using Both HTTP and HTTPS in same WCF Binding - Changing <security mode="Transport"> to <security mode="TransportCredentialOnly"> In Code

I have an autogenerated binding from a WSDL file in a Visual Studio project that looks like this:
<binding name="XServiceSoap"
maxBufferPoolSize="16777216"
maxBufferSize="16777216"
maxReceivedMessageSize="16777216">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm"
proxyCredentialType="None"/>
</security>
</binding>
and the client looks like this:
<endpoint address="http://uri/XService.asmx"
binding="basicHttpBinding"
bindingConfiguration="XServiceSoap"
contract="XService.XServiceSoap"
name="XServiceSoap"/>
In the code, I'm easily able to change the endpoint address (and credentials) using
using WPFApp.XService;
var xServiceSoapClient = new XServiceSoapClient("XServiceSoap");
xServiceSoapClient.Endpoint.Address = new EndpointAddress(NEW_ENDPOINT);
xServiceSoapClient
.ClientCredentials.Windows.ClientCredential.UserName = NEW_USERNAME;
xServiceSoapClient
.ClientCredentials.Windows.ClientCredential.Password = NEW_PASSWORD;
But if I try using a non-http endpoint, I got thrown an exception The provided URI scheme 'https' is invalid; expected 'http'.
This error makes sense to me (ex. from here), but I want the same binding to support both HTTP and HTTPS when I change the endpoint (test vs prod server). How do I do this in code with the built in soap client? I don't see an option in the xServiceSoapClient to change the security mode, ex. I don't see a XServiceSoapclient.Security.Mode option where it can be changed.
I can confirm when I change the app.config manually from TransportCredentialOnly to Transport, https connections work without throwing an exception.
Thank you!
The constructor for the WCF client has an overload that takes two parameters, binding and endpoint
you can use this overload to pass your desired security mode:
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
var xServiceSoapClient = new xServiceSoapClient(binding, "XServiceSoap");
This is the general idea, using the designated object rather then the service client object that is not meant for the above kind of manipulations.

consuming webservice using https protocol

I'm developing an application where i have to consume webservice developed in Java using http protocol.
I'm developing the application using C#.NET winforms. Everything works fine until now. The webservice is now using SSL security hence the service protocol changed from http to https. I'm facing issues while accessing the https webservice.
I tried accessing the https webservice from the SoapUI by providing the Authenticaion Parameters (UserName and Password) from the Auth tab as shown below:
It is working fine from SoapUI.
but wen i provide the Authenticaion parameters from code as below its not working:
client.ClientCredentials.UserName.UserName = "admin";
client.ClientCredentials.UserName.Password = "*******";
I'm using Security Mode as : TransportWithMessageCredential
and
ClientCredentialTtype as : Basic
My App.Config file is as below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<client>
<endpoint address="https://xyz:8001/HelloWorldAPI/HelloWorldWebService"
binding="wsHttpBinding"
bindingConfiguration="myhttpsbinding" contract="API.HelloWorldWebService"
name="HelloWorldWebServicePort" />
</client>
<bindings>
<wsHttpBinding>
<binding name="myhttpsbinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
<system.net>
<defaultProxy useDefaultCredentials="true" />
</system.net>
</configuration>
My Code as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using testingtool.API;
namespace testingtool
{
class Program
{
static void Main(string[] args)
{
new APITool();
}
}
class APITool
{
UserInfo userinfo = new UserInfo();
HelloWorldWebServiceClient client = new HelloWorldWebServiceClient();
private bool ValidationCallBack(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
return true;
}
public APITool()
{
try
{
//Authentication parameters
client.ClientCredentials.UserName.UserName = "admin";
client.ClientCredentials.UserName.Password = "*****";
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(ValidationCallBack);
//client ClientCredentials # Application level
userinfo.userid = "myusername";
userinfo.password = "*****";
GetHelloWorldAPIVersionRequest request = new GetHelloWorldAPIVersionRequest();
APIVersionInfo versioninfo = new APIVersionInfo();
versioninfo.userinfo = userinfo;
request.APIVersionInfo = versioninfo;
APIVersionInfoResponse response = client.GetHelloWorldAPIVersion(versioninfo);
Console.WriteLine(response.Version);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
}
}
I'm getting following exception:
System.ServiceModel.Security.MessageSecurityException: The HTTP
request is unauthorized with client authentication scheme 'Anonymous'.
The authentication header received from the server was 'Basic
realm="EJBServiceEndpointServlet Realm"'. --->
System.Net.WebException: The remote server returned an error: (401)
Unauthorized.
EDIT: from the client i have verified with Fiddler the request window as below:
from the AUth tab it is saying that there is no Autherization Header present.
Fiddler Raw Request as below:
CONNECT 10.10.10.110:8001
HTTP/1.1 Host: 10.10.10.110:8001
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Any help wouldbe greatly appreciated.
Wondering if the issue could be with the binding, although hard to say for sure without reviewing the service configuration or seeing the successful soapUI request. (Note: you may want to include a snippet of the message, including the soap header, from the soapUI HTTP Log.)
In any case, you may want to make sure the service is really using ws-security (message level security) by trying the following binding:
<bindings>
<basicHttpBinding>
<binding name="httpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
You should change your app config:
<wsHttpBinding>
<binding name="myhttpsbinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" establishSecurityContext="false" negotiateServiceCredential="false"/>
</security>
</binding>
</wsHttpBinding>
On transport level you have no authentication, so <transport clientCredentialType="None" /> and on message level you have username|password authentication, so <message clientCredentialType="UserName"
are you going thru a proxy server? If yes, are the details entered into IE ? Are you able to browse https pages in IE? If yes, this should be picked up by each WebRequest that makes an https call as it needs to issue a CONNECT to the proxy server (which its doing) but in your call the Proxy-Authorization header is missing. This should be your focus - try a plain WebRequest to say https:// google.com and see what you get in fiddler.
You may have an issue with your proxy server forwarding on your request. Are you able to try https on a different network not behind a proxy?
Maybe this link can help.
It explains how to configure your endpoint in case of REST/SOAP WebServices
Can not call web service with basic authentication using WCF
I have given <security mode="Transport" /> inside binding and issue solved for calling HTTPS service. Full code:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ServiceSoap">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://yourlink/Service.asmx" binding="basicHttpBinding" bindingConfiguration="ServiceSoap" contract="EmployeeService.ServiceSoap" name="ServiceSoap"/>
</client>
</system.serviceModel>
Its binding issue you need to use custom binding here is example.
http://webservices20.blogspot.com/

How to supply both UserName and Client Certificate in WCF client (why does this example work)?

Consider a WCF service in which the intent is to have Client Certificates required at the Transport layer (Client Certificates set to "Required" in IIS). As well, there will be username authentication at the message layer.
Now I've seen this question already:
WCF Client Certificate AND UserName Credentials forbidden
and I can somewhat understand what's going on there and realize that inherently WCF does not allow both. I went through the same steps in code as the poster in the link referenced above and found the same result...the message-level UserName credentials were being passed (in the SOAP header in my case), but the Client Cert (despite being attached when the request client is viewed in VS debug) was not actually being processed by the endpoint.
So now comes the part that has me confused. I decided to hack it somewhat. I'm wondering why this works exactly like I'm wanting...it gets past IIS Client Cert requirement, the UserName gets passed to the WCF Service and all just works. Yet WCF does not allow me to do it just using WCF config files or code (that I can find). Why?
// sets up a proxy client based on endpoint config
// basically just here to get the URL.
this.InitializeSubmitClient();
// these get used to create the HttpWebRequest
string url = this.submitClient.Endpoint.Address.ToString();
string action = "SubmitCDA";
// this deserializes an XML file which is the "shell" of SOAP document and inject username/password into SOAP Security node
XmlDocument soapEnvelopeXml = XMLHelper.CreateSoapDocument(this.txtSubmitCdaXmlFile.Text, this.txtAdAccount.Text, this.txtPassword.Text);
HttpWebRequest webRequest = XMLHelper.CreateWebRequest(url, action);
// saves the SOAP XML into the webRequest stream.
XMLHelper.InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest);
// attach the cert
if (this.chkSendClientCert.Checked)
{
X509Certificate myCert = X509Certificate.CreateFromCertFile(#"C:\temp\CDX-IHAT_DevClientCert.cer");
webRequest.ClientCertificates.Add(myCert);
}
else
{
webRequest.ClientCertificates.Clear();
}
// begin async call to web request.
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
To further complicate matters, the WCF Service that this applies to is a BizTalk service.
Here's how I ended up doing it.
The Server Config:
<customBinding>
<binding name="CustomCDARequestEndpointBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="UserNameOverTransport" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
The Client Config:
<system.ServiceModel>
<bindings>
<customBindings>
<binding name="CustomBinding_ITwoWayAsync">
<security defaultAlgorithmSuite="Default"
authenticationMode="UserNameOverTransport"
requireDerivedKeys="true"
includeTimestamp="true"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
>
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ohBehave">
<clientCredentials useIdentityConfiguration="false">
<clientCertificate findValue="6D0DBF387484B25A16D0E3E53DBB178A366DA954" storeLocation="CurrentUser"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://myservice/CDASubmitService/CDASubmit.svc"
binding="customBinding" bindingConfiguration="SubmitDev" behaviorConfiguration="ohBehave"
contract="CDASubmitService.CDASubmit" name="SubmitDev" />
</client>
</system.serviceModel>
The key to getting it working was the <httpsTransport requireClientCertificate="true" /> element and the <security authenticationMode="UserNameOverTransport" element/attribute.
This configuration allowed me to submit a message to a WCF (BizTalk) service completely through configuration files, with no changes to actual code. It still allows me to submit to it VIA WebRequest as well, as shown above.
I have to give credit to this post:
WCF Client Certificate AND UserName Credentials forbidden
as well as this one:
Translate non-BizTalk WCF config into BizTalk WCF-Custom endpoint
for finally getting me on the right track. I always shied away from Custom Bindings in WCF because I assumed it was overkill, but they are really nothing crazy, just a way to supply more detailed config than is available out of the box.

How to pass user credentials to web service?

I am consuming a webservice using WSDL in windows application. When I try to use method, i get the following error:-
The HTTP request is unauthorized with client authentication scheme
'Anonymous'. The authentication header received from the server was '"
{"The remote server returned an error: (401) Unauthorized."}
I have user credentials but don't know how to pass it using c# code in windows application.
Here is the how it is working for me:-
Config file setting looks like this:-
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="bindingName" >
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://10.10.10.10:1880/testpad/services/Testwebservice"
binding="basicHttpBinding" bindingConfiguration="bindingName"
contract=testService.GetData" name="test_Port1" />
</client>
</system.serviceModel>
</configuration>
and here i am passing user credentials:-
var ser = new GetDataClient();
ser.ClientCredentials.UserName.UserName = "userid";
ser.ClientCredentials.UserName.Password = "Pa$$word1";
You can try to genereate your service client proxy using the method mentioned here. Once you have an instance of your WCF client proxy, it will have a property ClientCreditials which you can populate as needed.
Hope this helps.

Categories

Resources