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.
Related
I am working on a simple site which has front-end and back-end server. Both servers are using the SAME SSL certificate. The SSL cert has DNS entries for FQDN and URL.
DNS=test.sample.com
DNS=FQDN01.dev.sample.com
DNS=dataservices.sample.com
DNS=FQDN02.dev.sample.com
Tech Stack
Front end = ASP.NET Core
Back End = .NET Core
IIS 10.
Windows 2016
When I call the data services from front-end it throws 404 error.
Then I removed certs from both the servers and added a new certificate which has only FQDN and no DNS for URL endpoint. Configured my code to call the data services using FQDN and magic - it started working!!!
Question - Are there any issues from .NET Core to use the same certificate on both servers? I am not IIS Admin or SME. Not sure how the bindings work. Any help will be greatly appreciated.
This is the snippet which calls the dataservices from front-end:
var handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => {
return true;
};
Client = new HttpClient(handler);
BaseUri = AppConfiguration.Configuration["DataServices:ServerConnection"];
I have created a self-hosted WCF RESTful service with basic http authentication that runs over https with a self-signed SSL certificate. Everything works fine. Users access the service operations via a web browser.
The problem is that my customer now wants the service users to authenticate with BOTH basic authentication (user name + password) AND a certificate. I have not been able to achieve this.
I have seen that it could be possible to have multiple authentication schemes in WCF 4.5. I have looked into this but to no avail.
I have also come across this post (see the last answer), but when I tried it I got this error:
"An exception occurred: HTTPS listener factory was configured to require a client certificate and the authentication scheme 'Basic'. Only one form of authentication can be required at once."
My configuration is done in code, and here is what it looks like (this is the version that works):
Uri baseAdress = new Uri("https://localhost:8446/");
WebServiceHost host = new WebServiceHost(typeof(RestService));
WebHttpBinding wb = new WebHttpBinding();
wb.Security.Mode = WebHttpSecurityMode.Transport;
wb.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
host.AddServiceEndpoint(typeof(IRestService), wb, baseAdress);
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
host.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true });
host.Open();
Thanks for any tips,
Multi auth on a single endpoint is for web hosted scenarios (not selfhost). You can configure this in web host by saying clientCredentialType='InheritFromHost' and set all the auth schemes that you want to set for that particular endpoint in vdir authentication. Check out this documentation for title "Multiple authentication support".
Multiple Authentication Support
Support has been added to support multiple authentication modes, as supported by IIS, on a single WCF endpoint when using the HTTP transport and transport security. IIS allows you to enable multiple authentication modes on a virtual directory, this feature allows a single WCF endpoint to support the multiple authentication modes enabled for the virtual directory where the WCF service is hosted.
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;
}
}
}
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 have a Self-Hosted (Console App) WCF REST service with the following binding:
WebMessageEncodingBindingElement
HttpsTransportBindingElement (ManualAddressing=true, KeepAliveEnabled=true, AllowCookies=false, HostNameComparisonMode=Exact)
This is exposed over an HTTPS URL ("https://mylaptop/myendpoint")
I have a self-signed certificate issued to "mylaptop" that I assign using myServiceHost.Credentials.ServiceCertificate.SetCertificate. The certificate is added successfully, and the ServiceHost opens successfully (no exceptions). If I type "netsh http show servicestate", I can see that there's a successful registration at https://mylaptop/myendpoint with HTTP.SYS
However, when I issue a GET to the endpoint, it doesn't work. It seems like the socket is dropped even before a valid HTTP response is obtained. (FireFox says "connection to the server was reset", IE says "cannot display the webpage", and if I do the request through Fiddler it says "connection was forcibly closed by the remote host").
Everything works fine when I use HTTP instead of HTTPS.
Any idea what could be going wrong in the HTTPS case?
You probably need to use httpcfg.exe to reserve your endpoint with HTTP.SYS correctly with a configured X.509 certificate for SSL. The steps to get it done are documented here.
Or use HttpCfgGui- a much friendlier interface to setting up the server certs w/ HTTP.SYS. This is a must-install on all my servers that do HTTP w/ WCF.