I've a WCF service hosted on IIS 7 which uses a self-signed certificated to provide HTTPS connection.
The server is located in US but the client that consumes it are in the middle east. In order the clients to be able to use the service I had to change the server's time and Timezone to the country that the clients are.
This configuration was working (for almost a year now) but from 2 days ago the some of the clients stop working and getting the following error:
An error occurred when verifying security for the message.
If I change the server time to one hour before the not working clients these clients will be able to use the service but the previously working ones stop working and receiving the same error.
Does anybody know how can I fix this problem.
Thanks
There's two sides you can tackle this from: if you have AppFabric or something similar installed in your IIS, you can start writing trace files for your service. That should give you a pretty good picture of what went wrong on the server side.
On the client side, without touching the server, you may want to check if you can get more specific errors than just "something with security didn't work" that .NET likes to give. Try to write a console application calling your service and check on the SSL errors there.
namespace ServiceTestConsole
{
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
internal class Program
{
internal static void Main()
{
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
// add a service reference and call your service here
Console.WriteLine("Press any key to continue");
Console.ReadKey();
}
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
{
Console.WriteLine("SSL Policy Error(s): " + sslpolicyerrors);
return true;
}
}
}
Related
I've seen about 1000 posts on StackOverflow, blogs, and other sites, but none have been able to shed light on the problem I'm having.
Setup:
WCF self-hosted service (NOT IIS) using .NET Framework 3.5 (sorry, can't upgrade), WebHttpBinding
A single endpoint using WebGet in the interface
NO .config files -- everything is being created programmatically
The service is bound to a custom port and NOT sharing a port with any other service or website
Deployment targets:
XP SP3, 2003, Vista, 7, 8, 2008
Problem: On Vista, 7, 8, 2008 I have no problems. I'm getting the service up and running on HTTP as well as HTTPS with a self-signed certificate bound to localhost as well as the machine name on a custom port.
BUT on XP, I can only get things working on HTTP, so I know the service itself is working properly. On HTTPS, I'm not able to make a connection because of an SSL failure.
If I hit the URL directly in a browser, I see an SSL exception.
In IE, it gives a warning that the certificate is not trusted. When I allow the exception, it gets to the service and executes. If I add https://localhost to trusted sites, I no longer see the warning and can hit the URL without issue.
When I hit the same URL in Chrome, I get an Error 107 ERR_SSL_PROTOCOL_ERROR and I cannot bypass it.
In Firefox, I get a ssl_error_rx_record_too_long error and it also cannot be bypassed.
I've gone through several permutations of certificates and methods of assigning them to the service (httpcfg, etc.), all with the same results (or worse). So, instead of going back and forth trying to figure out what I've done and picking apart my existing settings, I have 2 questions:
Is it even possible to create a trusted certificate on XP for localhost WITHOUT IIS and...
What's the best way to bind it to a self-hosted WCF service with everything being done programmatically? I repeat this because other attempts to get assistance on these issues invariably leads to folks telling me what to put in a config file.
Things to keep in mind: I already have this all working fine on Windows versions > XP/2003 under SSL with self-signed certificates, so I know the fundamentals are somewhat sound. I just can't seem to get the certificate set up under XP for localhost.
My answer is based on an assumption that if you were making WCF call from client code, you will get "Could not establish trust relationship for the SSL/TLS secure...." exception.
If that's true, I would recommend that you implement custom ICertificatePolicy and use it while making the WCF call. Here's a sample implementation.
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace Foo.bar
{
public class MyCertificatePolicy : ICertificatePolicy
{
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
{
//Due an Invalid Certificate used in the site, we must return true to all invalid SSL Request. Alternately, write the logic to validate the server certificate
return true;
}
public static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
//Due an Invalid Certificate used in the site, we must return true to all invalid SSL Request
return true;
}
}
}
In my case i do it like this:
ServiceHost host = new ServiceHost(typeof(MyRequests), baseAddresses);
Then setup my metadata:
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpsGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
For Windows authentication and message secutiry i create and setup my ws binding:
WSHttpBinding b = new WSHttpBinding(SecurityMode.Message);
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
b.MaxReceivedMessageSize = Int32.MaxValue;
// auth setup
X509ClientCertificateAuthentication myAuthProperties =
host.Credentials.ClientCertificate.Authentication;
myAuthProperties.IncludeWindowsGroups = true;
// add endpoint
ServiceEndpoint endpnt = host.AddServiceEndpoint(typeof(IMyRequests), b, "MyService");
//GO!
host.Open();
I hope this will be helpful.
I am connecting to a 3rd party API. They have provided me with a WSDL file which i have added by using a web reference. Is it possible to connect via SOAP and send multiple messages across the same connection?
I have created the client from the proxy classes but there doesn't appear to be a Open() or a Close() method. Does the client connect and disconnect when a method is called?
SampleService client = new SampleService
client.SampleMethod();
Edit:
I have added a "Service Reference" from the WSDL file. The client is constructed from the "PortType" in the WSDL file. There inst a Close() or a Abort() method. The only method on SampleService.client is SampleMethod()
They have provided me with a WSDL file which i have added by using a web reference.
In such case you are not using WCF but ASP.NET WebServices client. In your case it is probably not a big difference but ASP.NET WebServices are mostly for backward compatibility when moving legacy code to new version of .NET framework. Today you should rather use Add Service Reference to use WCF as other also recommended.
SOAP Connection WCF
There is nothing like SOAP connection. SOAP is application protocol tunneled through some transport protocol. In your case the transport protocol is HTTP.
I have created the client from the proxy classes but there doesn't
appear to be a Open() or a Close() method. Does the client connect and
disconnect when a method is called?
You don't need to call Open or Close. This is all handled in low level layer of communication stack. By default all HTTP client applications use something called persistent connection. It means that when you access some host for the first time TCP connection is established between your client computer and server hosting target resource. Subsequent calls reuse the same connection. If the connection is not in use for some predefined time (both client and server can have different timeouts) the connection is closed and next time the client wants to call the server it will create a new connection (again you don't need to bother with this).
So calling the method on SOAP proxy in your application can automatically open connection if no one exists. You don't need to explicitly close the connection but you should dispose the proxy to release its resources. Connection itself can live after the proxy was disposed and can be reused by another proxy.
You should add a service reference (WCF) instead of using the outdated Web Reference.
Then follow the bellow pattern:
try
{
SampleServiceClient client = new SampleServiceClient();
client.SampleMethod();
//... Make as many calls to as many methods as you like.
client.SampleMethod();
client.Close();
}
catch (Exception e)
{
//client is unusable once an exception occurs.
...
client.Abort();
throw;
}
You need to use the Add Service Reference rather than Add Web Reference. And once added you can invoke the service as shown:
using(SampleServiceClient client = new SampleServiceClient())
{
var response = client.SampleMethod();
}
Hope that helps.
I have an N-Tier app which built as follows:
On the server side in IIS7 is sitting an ASP.Net Application that exposes methods on a WCF Service. Those methods talk to the DB using EF4. Clients written in Silverlight 4.0 are calling the methods on the WCF service.
The WCF Service exposes this method :
[OperationContract]
void DeleteItem(int i_ItemId);
I am new to seuring WCF services, but I think these next observations are true (correct me if I am wrong):
1) If I leave this method/service as is, anyone that knows that my service is sitting at http://www.mysite.com/myservice.svc could just open up VisualStudio, open "Add Service Reference" and start making calls to DeleteItem().
2) If I try to solve the above by remove the MEX endpoint, Calls to the service can still be made using some manual coding.
So trying to solve those two problems I started learning about some built-in security features in WCF. After a quick look I figured i can use the following binding Configuration so that a username and password are needed for making calls to the service :
<wsHttpBinding>
<binding name="RequestUserName" >
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
Trying to adopt this solution, the first thing that came to my mind was: When a user logs in the client, the client uses the user's name and password as the credentials for the WCF call. At the server-side, those credentials are validated against the DB.
The problem now, is that those credentials (userName and password) are , of course, known to the user, so he can just use them to call DeleteItem() in the ways I already mentioned.
From here I came up with two solutions :
1) Instead using userName and password as credentials, I will use a hard-coded key in the client. Scrambling the Dll's inside the XAP could prevent someone from obtaining this key.
2)When a user logs in the client, the server sends some kind of a temporary token (GUID or something), which the client can use to authenticate it's calls during this session of communication (Let's say, until the user closes the client).
My question are :
What the security level that the first solution provides and how hard you need to work in order to crack it?
If the first solution is very trivial to crack, Is there a built-in way in WCF to manage the tokens system I mention in the second solution ?
Other solutions that can feet the scope are welcomed.
I'm not sure about security level you are asking about, but I wouldn't feel confident storing username and password in XAP file, obfuscated or not.
I can describe a solution that I've implemented in production.
Basically, I secure the application with standard Forms Authentication, but I don't use the redirects like you normally would with ASP.NET, I use Authentication Web Service that ships with ASP.NET Forms Authentication. That way my login goes through Silverlight controls. My app has a user store that I authenticate the Authentication Service against.
To hook to the Authentication Service, I do this in Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
AuthenticationService.Authenticating += new EventHandler<AuthenticatingEventArgs>(AuthenticationService_Authenticating);
}
void AuthenticationService_Authenticating(object sender, AuthenticatingEventArgs e)
{
try
{
bool authenticated = //Call your user store here.
e.Authenticated = authenticated;
}
catch (Exception ex)
{
e.Authenticated = false;
}
e.AuthenticationIsComplete = true;
}
You would secure the parts of the website like you would normally with forms using <authorization> element with <deny users="?">. The browser will handle all of the cookies for you. If you wanted to secure services, under Service/ folder you would deny not authenticated users to it.
This MSDN Post talks about the solution in more detail.
I need to call a WCF service programmatically. The service may be hosted with either NTLM or Kerberos authentication and needs to work under either. That is, if connecting to the service via Kerberos fails, then it should fall back to NTLM.
Here's the code I'm using for Kerberos auth (if relevant, the service is hosted in SharePoint 2010 and is being called from a web part):
public static SiteMembershipSvc.SiteMembershipServiceClient InitialiseSiteMembershipService(string url)
{
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
url = url.EndsWith("/") ? url + SiteMembershipAddress : url + "/" + SiteMembershipAddress;
var endpoint = new EndpointAddress(url);
var proxy = new SiteMembershipSvc.SiteMembershipServiceClient(binding, endpoint);
proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
return proxy;
}
Calling a method on the proxy when run in an NTLM environment gives the error:
The HTTP request is unauthorized with
client authentication scheme
'Negotiate'. The authentication header
received from the server was 'NTLM'.
Note: The URL may be in another web application on another server. I can't check what authentication the web part's web app runs under and assume it is the same as where the WCF service is hosted.
How can I (automatically or manually) ensure authentication falls back from Kerberos back to NTLM on failure?
Update:
As mentioned, the authentication error occurs when a web method is called. However I don't want to wait that long as there are several web methods in the service called from several places. I'd like to test the authentication at the point where the proxy is configured (in the code snippet above).
I've tried using proxy.Open() but that doesn't seem to cause the failure.
This is a bit off a curveball, but why is it falling back to NTLM. I've had significant difficulty with security in active directory and WCF all related to service principal names (SPNs).
Kerberos will fail if you are running the service as something other than Network Service unless you have an SPN declared in the domain for your service. To set the SPN you need the windows server administrative kit, which has the command setspn.
setspn -A HTTP\machinename domain\service_account
This will then allow Kerberos to share client credentials to your service within the domain.
Please do some reading, as you could break kerberos for any other services running on the same box depending on your setup.
(I recognize the original post is very old.)
Can you use something other than BasicHttpBinding (like WsHttpBinding)? According to this article, BasicHttpBinding is the one exception to the binding objects, in that it does not automatically negotiate. This is why allowNTLM has no effect.
I had the same error msg which I posted about here and solved it by creating a dynamic endpoint like so:
public static SiteMembershipSvc.SiteMembershipServiceClient InitialiseSiteMembershipService(string url)
{
//create endpoint
EndpointAddress ep = new EndpointAddress(new Uri(string), EndpointIdentity.CreateUpnIdentity("MyDomain\WCFRunAsUser"));
//create proxy with new endpoint
SiteMembershipSvc.SiteMembershipServiceClient service = new SiteMembershipSvc.SiteMembershipServiceClient("wsHttp", ep);
//allow client to impersonate user
service.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
//return our shiny new service
return service;
}
I was running the WCF service as a specific Active Directory user rather than the default NETWORK_SERVICE.
Try setting:
proxy.ClientCredentials.Windows.AllowNTLM = true;
According to this, AllowNTLM is now obsolete - i'm not sure what the correct alternative is.
I guess you are using the full dns name of the server as the address of the service. Try using the NETBIOS name or the IP address. That should force it to use NTLM.
If you know what protocol the server is using you can configure your app to use either the full name or the ip.
Hope that works for you.
If your Kerberos fail it will automatically default to NTLM, you don't have to do anything special.
http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part1.html
http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part2.html
http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part3.html
I haven't been able to find a way to do this automatically. Instead I've added UI to the application where the type of authentication must be chosen.
I've got a Silverlight app that talks to an HTTPS web service.
On most machines it works fine, however, on some machines it fails consistently.
On the machines it fails on, I receive a SecurityException when making a WebClient request to the HTTPS web service. The SecurityException itself doesn't give me any clues as to why it's really failing:
WebClient client = ...;
client.DownloadStringCompleted += OnCompleted;
client.DownloadStringAsyc("https://somewebservice/foo");
...
void OnCompleted(object sender, DownloadStringCompletedEventArgs e)
{
Console.WriteLine(e.Error); // Prints SecurityException. Message = "Security error"
}
What are the possible reasons a Silverlight app will fail to call an HTTPS web service? How can I go about debugging this?
edit Still no answers -- is there any additional info I can give to help solve this problem?
We figured it out. The problem came down to cross-zone calls:
Our Silverlight app was hosted on foo.bar.com, which is in IE's regular Internet Zone (low trust).
Our web service was hosted on foo.ourcompany.com, which is in IE's Intranet Zone (high trust).
Silverlight apps cannot make web request calls from low security zones to higher security zones. See MSDN's article on Silverlight URL Access Restrictions for more information. In our case, going from Internet->Intranet was going from low trust to high trust, and so SL call failed with a SecurityException.
Opinion: Microsoft should provide information about why a SecurityException occurred during web request calls. This would have saved us much time and money.