I need to connect with an external service, and I have a problem with client authentication. The service requires a certificate, username and password with a request.
I'm using Windows Server 2008 R2.
I've received the PKCS#7 package with the certificates and imported:
SSL certificate (with only public key) to LocalComputer/Personal
Intermediate CA and Root CA to LocalComputer/TrustedRootCertificationAuthorities
I've enabled the TLS 1.0, 1.1, 1.2 clients in the Windows Register:
Windows Register
I'm trying to connect to the server both using the WCF client and web browsers (IE and Chrome).
WCF client (.NET 4.6.1):
App.config:
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ICalculator" >
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="endpointCredentialBehavior">
<clientCredentials>
<clientCertificate findValue="<thumbprint>"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
Program.cs:
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12;
using (var client = new ServiceClient())
{
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "pwd";
client.Open();
var response = client.DoSth();
}
And I get an error:
"Could not establish secure channel for SSL/TLS with authority ...".
Internet Explorer shows that:
IE error
Chrome shows that:
Chrome error
I also tried to debug it via Wireshark. For me, it is suspect that the 'Certificate' message from the client doesn't contain any certificate (neither using the WCF client or a web browser). Should the certificate be added here, and if yes what could be causing a problem with it?
Wireshark packages
I know that there are a lot of articles about TLS and authentication on stackoverflow and Google, but I went through a lot of them and didn't find any info about what I'm doing wrong.
We had better not specify the TLS version, configure your code to let the OS decide on. The specific TLS version used in the communication will be determined by both the server and the client environment.
https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls
Transport security mode requires that the trust relationship between the server and the client should be established before the communication.
In my opinion, the error typically indicates that the client doesn’t install the server certificate properly.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/transport-security-with-certificate-authentication
Only the server side add Servicemetadata behavior, we can access the service definition(WSDL).
Usually the client provides a certificate that is sufficient to indicate its identity and there is no need to provide a username/password again. Just like the generated client configuration, we only need to provide a certificate. How the service is specifically called, we must know the configuration of the server or the WSDL file.
Feel free to let me know if there is anything I can help with.
Related
My testing environment is:
1* Server :: SharePoint Site with WCF Service (in the ISAPI folder) and we configured the SSL and Cert on it.
1* Client :: Windows 10 with one Console Application to involve the WCF service
Testing (1) :all programs (WCF Service and Console Application) on the SharePoint Server
I created a Console application to invoke the WCF Service in SharePoint. Meantime, the Console application and WCF are on the same server. the outcome is everything works properly. It is successful to upload file into a Document Library.
Testing (2) :to simulate my client environment:
1* SharePoint :WCF Service
1* Windows 10 :Console application
the outcome failed and I got the error message:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'NTLM'
To be honest, I stuck here for a while. I also list out a portion of my source code for someone of experience to look at. I also posted some sample code and web.config in here.
Updated.
If we enable NTLM authentication, we have to enable the windows authentication.
Then when calling the service, we need to provide windows credential.
//it will use the binding and service endpoint address in the system.servicemode section.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
//windows account on the server.
client.ClientCredentials.Windows.ClientCredential.UserName = "administrator";
client.ClientCredentials.Windows.ClientCredential.Password = "abcd1234!";
try
{
Console.WriteLine(client.SayHello());
}
catch (Exception)
{
throw;
}
Auto-generated configuration.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService">
<security mode="Transport">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://vabqia969vm:21011/" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_IService" contract="ServiceReference1.IService"
name="WSHttpBinding_IService">
<identity>
<userPrincipalName value="VABQIA969VM\Administrator" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Feel free to let me know if the problem still exists.
I'm trying to connect to a soap web service over https, with soap11 and client certificate credentials using c# .NET 4.5. I use the tool of vs to add service reference and it create a soap client for me.
I have create a customBinding to accomplish https over soap11:
App.config
<system.serviceModel>
<bindings>
<customBinding>
<binding name="*****" closeTimeout="00:02:00" openTimeout="00:02:00" receiveTimeout="00:05:00" sendTimeout="00:05:00">
<textMessageEncoding messageVersion="Soap11"/>
<httpsTransport useDefaultWebProxy="false" requireClientCertificate="true" allowCookies="false" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="******************"
behaviorConfiguration="******************"
binding="customBinding" bindingConfiguration="******************"
contract="******************"
name="******************" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="******************">
<clientCredentials>
<clientCertificate findValue="******************" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
c# code:
var client = new IConsultService.ConsultServiceClient();
client.consult("1");
client.consult("1"); <!-- fail error 404 - there was not endpoint listening at
If I post without certificate in receive a tomcat 404, if I send the client certificate works ok the first time and it seems to me that I'm being redirected. The problem is that at the sencond request fail (404), also if I instantiate the client again fails. Only work again when stop the application and start it again. It seems that somethings is cached in the static stack of the program.
And since is a 404 my guess is that is something from the client certificate, if I request without the certificate the error is the same.
I tryied to create bindings programatically, set ClientBase.CacheSetting = CacheSetting.AlwaysOff without luck.
Edit1:
Using System.Net.HttpWebRequest happens the same, first request ok, second request 404.
Edit2:
It seems that there is something on the handshake first request send "Certificate, Client Key Exchange" and second request "Change cipher Key Exchange"
is there a way to force to close the channel and always send"certificate, client key exchange"?
Thanks
First, if we authenticate the client with X509certificate, we commonly need to establish the trust relationship between the server and the client, namely, we should install each other’s certificate in the trusted root certificate authority, and the client needs to provide an Identity to confirm the identity of the server (this is usually the public key of the server's certificate, or the host name)
<endpoint address="net.tcp://10.157.13.69:4386/" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_ITestService" contract="ServiceReference1.ITestService"
name="NetTcpBinding_ITestService">
<identity>
<dns value="vabqia969VM" />
</identity>
</endpoint>
Second, I suspect the issue is caused by the TLS version. Have you tried to upgrade the Dotnetframework of these project? at least 4.6.2 above. Please refer to the following document.
https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls
Feel free to let me know if the problem still exists.
I'm trying to write a client against a customer's SOAP webservice, using VS2013 and WCF. The webservice itself is behind their firewall, so they've established a proxy that I'm trying to contact. (The proxy seems to be implemented using MuleSoft's ESB, which may or may not be relevant.)
I've been given an https: url, and a username/password. When I load the url into a browser, I'm prompted for the username/password, and then I see the .wsdl. The .wsdl specifies an internal url that I can't access, but I figure that's for the actual site.
When I create a Service Reference in VS2013, using the proxy URL, I'm prompted for the username/password three times, then I get a proper client, settings in app.config, etc.
The generated bindings in the app.config are for a basicHttpBinding with security mode Transport, and an endpoint address pointing to that inaccessible internal url.
So, from the generated bindings, I:
Replace the inaccessible internal url with the proxy url I've been given.
Change the security mode to "TransportWithMessageCredentials"
<bindings>
<basicHttpBinding>
<binding name="MyCustomersServiceSoapBinding">
<security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName" />
</security>
</binding>
<binding name="MyCustomersServiceSoapBinding1" />
</basicHttpBinding>
</bindings>
Replace the ClientCredentials with username and password:
using (var client = new MyCustomersServiceClient())
{
var loginCredentials = new ClientCredentials();
loginCredentials.UserName.UserName = "ausername";
loginCredentials.UserName.Password = "apassword";
var defaultCredentials = client.Endpoint.Behaviors.Find<ClientCredentials>();
client.Endpoint.Behaviors.Remove(defaultCredentials);
client.Endpoint.Behaviors.Add(loginCredentials);
var myData = new MyData
{
};
var result = client.receiveData(myData);
}
When I run it, I get an exception:
Could not establish secure channel for SSL/TLS with authority 'xxxxx.com'.
Browsing around, most of what I find suggests problems with ssl certificates, but I'm not sure that makes sense. If that were the case, I'd expect to see issues when I view the .wsdl through the browser. And I thought that by removing the default client credentials, I'd be bypassing the certificate check. And I am seeing a few posts about more obscure problems that result in this same error message.
I've turned on SOAP message logging, but that's provided me with no information. It shows the failed outgoing message, but nothing of use.
So I've been looking at the traffic in Fiddler. I see two messages, an HTTP message to "Tunnel to" with Result 200, and an HTTPS message to the proxy url with Result 401.
At this point, I see two possibilities:
I need to install an SSL certificate, the way the error message would suggest, or
the problem is simply that I'm not providing the username/password to the service in a way that it understands, and it's rejecting my attempt to connect.
I'm leaning towards the latter. My problem? I know nothing about the system that's hosting the service. I'm passing username/password in what I thought was the usual mechanism for WCF, and it's not working.
So, finally, the questions:
Have I misled myself, and I do need to be messing about with SSL certificates?
If not, what do I do in WCF to pass a username/password to an HTTPS webservice, hosted by MuleSoft
ESB? (Mule EE Core Extensions/3.5.1, if that helps).
Not sure if the issue I encountered shares the same cause as your issue, but just in case I can help someone with this, adding requireClientCertificate=true solved my problem:
<bindings>
<customBinding>
<binding name="bindingName">
...
<httpsTransport requireClientCertificate="true"/>
</binding>
</customBinding>
</bindings>
I had the same error message, but the web service I'm consuming is over HTTPS and requires a SSL certificate as authentication.
Many endpoints have been disabling TLSV1.0 and TLSV1.1 recently
try:
CURL https://<<service host>> -v -TLSV1.0
and
CURL https://<<service host>> -v -TLSV1.2
For instance, https://www.comodo.com doesn't allow TLSV1.0 or TLSV1.1 but does allow TLSV1.2.
I'm tearing my hair out on this one, I have a WCF service that I can call through the browser and it works fine, when I call it from the web application with the below method I get a (401) Unauthorized error. And the service does not get called. What's more, when I run my web application from my local machine (debug mode using IIS Express) pointed at my dev server (IIS7) it works but when I deploy my web application to the dev server and point it to the dev server services it fails wit the 401 error. I think this is something to do with IIS7 but I'm not 100% sure and help would be super useful.
I have looked online for the answers but thus far the best I have found is this.
My service call is as follows:
var request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "GET";
request.ContentType = "application/json; charset=utf-8";
request.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
request.Credentials = CredentialCache.DefaultCredentials;
WebResponse responce = request.GetResponse();
Stream reader = responce.GetResponseStream();
var sReader = new StreamReader(reader);
string outResult = sReader.ReadToEnd();
sReader.Close();
var result = (T) JsonConvert.DeserializeObject(outResult, typeof (T));
return result;
My configuration for the service looks like this :
<service name="RGMPServices.Householding.Services.AccountService" behaviorConfiguration="Default">
<endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IAccountService" />
</service>
<service name="RGMPServices.Householding.Services.HouseholdService" behaviorConfiguration="Default">
<endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IHouseholdService" />
</service>
<service name="RGMPServices.Householding.Services.UserService" behaviorConfiguration="Default">
<endpoint address="" kind="webHttpEndpoint" endpointConfiguration="SecuredHttpEndpointBinding" contract="RGMPServices.Householding.Contracts.IUserService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webBehaviour">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="SecuredHttpEndpointBinding" helpEnabled="true" automaticFormatSelectionEnabled="true">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</standardEndpoint>
</webHttpEndpoint>
</standardEndpoints>
I have put some logging on the client service call, just before I call the service, the response is:
DEBUG 2013-10-01 13:15:13,569 452ms ServiceGetSingle - Passing Login: MYLANDOMAIN\MYLANUSERNAME
ERROR 2013-10-01 13:15:13,631 514ms ServiceGetSingle - ERROR Calling ServiceGetSingle with user credentials login: MYLANDOMAIN\MYLANUSERNAME
System.Net.WebException: The remote server returned an error: (401) Unauthorized.
at System.Net.HttpWebRequest.GetResponse()
at Householding.Common.ServiceHelper.ServiceGetSingle[T](String url)
The code looks like:
logger.Debug("Passing Login: "
+ System.Security.Principal.WindowsIdentity.GetCurrent().Name)
Even when I set the AppPool for my website to my domain account it is still not authorising me to access the WCF Service, but again: it's working for the browser. So weird!
It seems likely you're a victim of the double-hop issue when using Integrated Windows Authentication (IWA) and Kerberos. The first hop is from your browser to the web application; the second hop is from your web application to the WCF service.
Here are some resources that explain the issue more fully, and may offer a solution:
IIS, Windows Authentication and the Double Hop issue
Using Integrated Windows Authentication (IWA) in a Distributed Application Architecture
Understanding Kerberos Double Hop
You can configure Active Directory to support Kerberos delegation (usually the infrastructure guys don't like this), or you could turn off impersonation and use a "service" account for the web application and IIS app pool that can authenticate with the WCF service on behalf of the end user.
What are the default credentials on the Dev server? Try doing a log right there and see what you get.
This is what I suspect: Running locally, the credentials are YOUR windows creds. When you call the dev server from dev, the credentials would be whatever account the website is running under. If that particular account doesn't have access, then it would blow up.
How they said before, this looks like an impersonation problem.
Have you tried to start the client program with "run as" to change the credentials?
Additionally you can change this line of code
request.Credentials = CredentialCache.DefaultCredentials;
to
request.Credentials = new NetworkCredential("MyUsername", "MyPassword");
And see if it works. Also you need to create the account "MyUserName" with "MyPassword" on the web server to make it work.
These errors can be caused when the authenticated user does not have access to the physical path where the WCF service is hosted. On the dev server, open up IIS Manager and navigate to the virtual directory for the service. On the right in the Actions bar, click on "Basic Settings". Below the "Physical Path" text box, click "Connect as...". Choose "Specific User" and try setting it to a user account that you know has rights to the physical folder on the dev server. Typically, this would be a service account whose password does not expire.
When running from the browser, the browser is sending your auth credentials. Also, iis express will run as the logged in user, so this is also sending your credentials. Iis is different, it will be running as a local account. Even if you have authentication on your front end iis, that will not be passed to the backend. Windows impersonation tokens are limited in the number of hops allowed, usually 0. This is done to prevent exactly what you are doing.
If you want front end authentication to flow to the backend then you should probably do the authentication yourself and grab user/pass on the way through. Alternatively, if you do the authentication yourself you can create an impersonation token that allows a hop to another machine and it should work.
I have a WCF service hosted by a Windows Application of my own. I want the service to authenticate the client at the transport level using the client's Certificate and when the client communicates with the service, it must pass its Kerberos Ticket of the client's user account. Looking around on the internet i found this configuration for the service:
<bindings>
<customBinding>
<binding name="Kerberos (MsgHeader) over Transport (Certificate)">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="KerberosOverTransport">
<secureConversationBootstrap />
</security>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
They say that this configuration worked with Microsoft's help. Now, i tried to duplicate this configuration through code, so i used the following snippet:
BindingElementCollection elementCollection = new BindingElementCollection();
elementCollection.Add(SecurityBindingElement.CreateKerberosOverTransportBindingElement());
elementCollection.Add(new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.Soap11 });
elementCollection.Add(new HttpsTransportBindingElement() { RequireClientCertificate = true });
return new CustomBinding(elementCollection);
I configured the endpoint with this binding along with a valid certificate bound to the port. I then added a service reference at the client project in Visual Studio.
To this end, everthing is working great! I also configured the client to send its certificate when communicating with the service, this also worked fine. However, when I try to invoke any method of the service, I get the following error:
An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
And when I examined the inner exception, I found the following error:
An error occurred when processing the security tokens in the message.
Any help, please? Do i need to configure the client further to send its Kerberos token, or what?
P.S. The service and client machine clocks are synchronized. In addition, the service and the client are on two different machines joined to a domain.