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/
Related
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.
I have a SOAP that I'm trying to work with but I encounter the next problem that I didn't find the solution in similar questions:
The SOAP has to be called via HTTPS and with Windows credentials.
What I tried to do in the app config is the next things:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpsBinding>
<binding name="WebServiceSoap">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" />
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpsBinding>
</bindings>
<client>
<endpoint address="https://someapp/SDK/WebService.asmx"
binding="basicHttpsBinding" bindingConfiguration="WebServiceSoap"
contract="someapp_contract.WebServiceSoap" name="WebServiceSoap" />
</client>
</system.serviceModel>
</configuration>
And the error that I get is:
The username is not provided. Specify username in ClientCredentials.And the error
I tried a differt confingruation too with basicHttpBinding
and
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
But than the error that is I get is(which is a very logical error):
provided URI scheme 'https' is invalid; expected 'http'...
Did anyone have the same issue in the past?
Thanks in advance.
Max
P.S:
If I do
<security mode="Transport">
instead of
<security mode="TransportWithMessageCredential">
I get the next error:
The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.
Following the settings above.
When I use:
<security mode="Transport">
and get the error:
The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.
What I did are the next changes.
1) first at the app.config
<security mode="Transport">
<transport clientCredentialType="Windows" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
Than at the code:
public static class Ssl
{
private static readonly string[] TrustedHosts = new[] {
"YourServiceName",
"YourServiceName2"
};
public static void EnableTrustedHosts()
{
ServicePointManager.ServerCertificateValidationCallback =
(sender, certificate, chain, errors) =>
{
if (errors == SslPolicyErrors.None)
{
return true;
}
var request = sender as HttpWebRequest;
if (request != null)
{
return TrustedHosts.Contains(request.RequestUri.Host);
}
return false;
};
}
}
And:
YourContractInstance.ClientCredentials.Windows.ClientCredential.UserName = ***;
YourContractInstance.ClientCredentials.Windows.ClientCredential.Domain = ***;
YourContractInstance.ClientCredentials.Windows.ClientCredential.Password = ***;
YourContractInstance.ClientCredentials.Windows.AllowNtlm = true;
All of this workaround was made because I have some problem with the certificate|DNS configuration on the host of the SOAP service that I'm not allowed to access so I had to add the Service reference by IP.
I have created following test application to retrieve data from Service Now.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace ServiceNowConnection
{
class Program
{
static void Main(string[] args)
{
Service_Now_Reference.getRecords records = new Service_Now_Reference.getRecords();
records.sys_user = "XXXXXXX";
Service_Now_Reference.ServiceNowSoapClient proxyUser = new Service_Now_Reference.ServiceNowSoapClient();
proxyUser.ClientCredentials.UserName.UserName = "XXXXXX";
proxyUser.ClientCredentials.UserName.Password = "XXXXXX";
Service_Now_Reference.getRecordsResponseGetRecordsResult[] result = proxyUser.getRecords(records);
foreach (var item in result)
{
Console.WriteLine(item.sys_created_by);
}
Console.ReadLine();
}
}
}
This connects to the service now application correctly with no errors, but when I try to print retrieved data it gives me keys instead string type answers.
For some properties it gives correct values (example sys_updated_by)
how can I avoid this situation.
The way you need to do it.
If you are using RESTfull API in C# of cause you can do it by sending direct HTTPS call to service now.
But things getting complicated when you are using SOAP api
While you're importing web service to your program it includes following XML codes to the App.config or Web.config depending on your application intention(Web application or stand alone Application).
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ServiceNowSoap">
<security mode="Transport" />
</binding>
<binding name="ServiceNowSoap1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://XXXXXXXX.service-now.com/service_subscribe_sys_user_list.do?SOAP"
binding="basicHttpBinding" bindingConfiguration="ServiceNowSoap"
contract="ServiceReference1.ServiceNowSoap" name="ServiceNowSoap" />
</client>
</system.serviceModel>
First of all if you are expecting to have big set of records to be retrieved you need to increase the size of Max Received Message Size.
for that you need to edit the tag like following
<binding name="ServiceNowSoap" maxReceivedMessageSize="2000000000">
Next part is adding displayvalue=all to the URL.
We can't edit end point url withing the XML it self instead you can remove the URL and add it as a key value. But still you cant add parameters to the url with & sign you need to store values as separate keys and combine it withing the program to get the full URL
Final XML will be like this
<configuration>
<appSettings>
<add key="serviceNowUrl"
value="https://XXXXXXXX.service-now.com/service_subscribe_sys_user_list.do?"/>
<add key="displayvalue" value="displayvalue=true"/>
<add key="protocol" value="SOAP"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ServiceNowSoap" maxReceivedMessageSize="2000000000">
<security mode="Transport">
<transport clientCredentialType="Basic" proxyCredentialType="Basic"
realm="">
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
<binding name="ServiceNowSoap1" />
</basicHttpBinding>
</bindings>
<client>
<endpoint binding="basicHttpBinding" bindingConfiguration="ServiceNowSoap"
contract="Service_Now_Reference.ServiceNowSoap" name="ServiceNowSoap" />
</client>
</system.serviceModel>
</configuration>
You can assemble the url as follow
string url = ConfigurationSettings.AppSettings["serviceNowUrl"];
string protocol = ConfigurationSettings.AppSettings["protocol"];
string displayvalue = ConfigurationSettings.AppSettings["displayvalue"];
System.ServiceModel.EndpointAddress endpoint = new System.ServiceModel.EndpointAddress(string.Format("{0}{1}{2}", url, protocol, displayvalue));
If you update the URL you are using to get the WSDL and make requests to, to include 'displayvalue=all' the response will include display names as well as sys_id values (think foreign_key) of referenced records.
For more info check out: http://wiki.servicenow.com/?title=Direct_Web_Services#Return_Display_Value_for_Reference_Variables&gsc.tab=0
Thanks,
Bryan
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.
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.