I've written both a WCF client and a remote internet WCF server.
The remote WCF server is running WPF hosted in a traditional Windows Service wrapper (i.e. not IIS).
Currently, its working perfectly with basic HTTP binding. I'm using Visual Studio 2010 + .NET 4.0 + C#.
Can anyone point me in the direction of the right steps to alter my code so that I can add username + SSL authentication?
EDIT:
At the service end, I've overridden UserNamePasswordValidator as follows:
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Console.WriteLine("Got to here");
}
}
At the service end, I've specified a link to the username validation class:
ServiceHost serviceHost = new ServiceHost(typeof(PhiFeedServer.PhiFeed)); // ,baseAddress);
const bool passswordAuthentication = true;
if (passswordAuthentication)
{
// These two lines switch on username/password authentication (see custom class "CustomUserNameValidator" in common file PhiFeed.svc.cs)
// See http://msdn.microsoft.com/en-us/library/aa354513.aspx
serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNameValidator();
}
// Start the service
serviceHost.Open();
At the client end:
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:20000/PhiFeed?wdsl");
BasicHttpBinding serviceBinding = new BasicHttpBinding();
serviceBinding.ReceiveTimeout = new TimeSpan(0, 0, 120);
proxy = new PhiFeedClient(serviceBinding, endpointAddress);
proxy.ClientCredentials.UserName.UserName = "myusername";
proxy.ClientCredentials.UserName.Password = "mypassword";
However, when I run everything, it never even calls the username validator - whats going on?
If i am getting this right, you will need to play around with service behaviour. I did that in 3.5 sp1 it should be the same in 4.0 i think.
read this:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/7d589542-277a-404e-ab46-222794422233/
Aha! Found the solution to my problem.
Microsoft provides example code which demonstrates how to add username/password + SSL authentication to a console app.
Search for "Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4", download, unzip into C:, then run the sample here:
C:\WF_WCF_Samples\WCF\Extensibility\Security\UserNamePasswordValidator\CS
Related
I'm kind of new to the whole WCF and SOAP topic so please be kind.
I'm using a generated SOAP Client with .net6. In another project we successfully worked with the same Web Service using the old .net Framework 2.0 Web References and the same credentials.
Strange enough everything seemed to work fine at first. Until I realized, that it does not use the given credentials to authenticate. Instead it authenticates with my own domain user.
I also tried to get it to work with explicitly setting the binding with a BasicHttpBinding but I only could get the same broken logic to work or I got various authentication/protocol/security errors.
So it seems the authentication is basically working. It just doesn't use the provided credentials. So my question is: How can I configure it to work with the provided identity?
I also found out that it might have anything to do with a cached Windows token. But how can I get rid of it. How to prevent caching in the first place?
EDIT:
Specified the variable types explicitly.
string url = "http://someServer/AdministrationService.asmx";
AdministrationServiceSoapClient client = new AdministrationServiceSoapClient(
AdministrationServiceSoapClient.EndpointConfiguration.AdministrationServiceSoap,
url);
WindowsClientCredential credential = client.ClientCredentials.Windows;
credential.ClientCredential.UserName = "username";
credential.ClientCredential.Password = "password";
credential.ClientCredential.Domain = "DOMAIN";
GetServerInfoRequest getServerInfoRequest = new GetServerInfoRequest
{
// some stuff set here
};
GetServerInfoRequest getServerInfoReply = await client.GetServerInfoAsync(getServerInfoRequest);
As far as I know, BasicHttpBinding has security disabled by default, but can be added setting the BasicHttpSecurityMode to a value other than None in the constructor. It can be configured according to the instructions in BasicHttpBinding and BasicHttpBinding Constructors.
By default, setting up client credentials involves two steps: determining the type of client credential required by the service and specifying an actual client credential, as described in this document.
After waiting a day it is working. It seems that the cached credentials became invalid somehow.
Strange enough the simple service creation from above is not working anymore. Instead I have to use the following.
var client = new AdministrationServiceSoapClient(
new BasicHttpBinding()
{
Security = new BasicHttpSecurity()
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Message = new BasicHttpMessageSecurity()
{
ClientCredentialType = BasicHttpMessageCredentialType.UserName,
},
Transport = new HttpTransportSecurity()
{
ClientCredentialType = HttpClientCredentialType.Windows,
ProxyCredentialType = HttpProxyCredentialType.Windows,
}
},
},
new EndpointAddress(url));
I have a WCF service. Now I would like to convert it to console application. Basically, it is a simple service. It only does 2 jobs depending on client request by PowerShell request.
Invoke-WebRequest "http://localhost:10000/backup/DoBackup?id=BackupClient1"
or
Invoke-WebRequest "http://localhost:10000/backup/DoBackup?id=BackupClient2"
My service listens on request for this 2 requests.
I have this WCF code:
myService service = new myService(_settings);
backupServiceHost = new WebServiceHost(service, _backupAddress);
WebHttpBinding binding = new WebHttpBinding();
string address = "";
ServiceEndpoint endpoint = backupServiceHost .AddServiceEndpoint(typeof(ISnapshotServiceContract), binding, address);
backupServiceHost .Open();
I can't use WebServiceHost and WebHttpBinding in my console app. What is the best replacement for this? I'm thinking to use simple HttpClient. But I'm not sure is it a correct selection?
why don't use command line parameters and avoid WebServiceHost altogether? You can then just compare your args[] from Main function (see e.g. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/main-and-command-args/) to determine if it is BackupClient1 command or BackupClient2 command.
If you want to take this concept further you can extract functionality of BackupClient1 command and BackupClient2 command into separate library and have frontends (WCF and command line) using same backend.
If you really need WCF service (as stated by comments below) please refer to this MSDN article; you will have to provide your configuration and figure out how to indicate that you wish to end the process in nice way but it should boil down to something like this:
String baseAddress = "http://localhost:10000/backup";
BasicHttpBinding binding = new BasicHttpBinding();
using (ServiceHost host = new ServiceHost(typeof(myService)))
{
host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress);
host.Open();
while(true) {
// Figure out how to exit this loop - e.g. listen to some command from service
}
}
I am quite new to WCF and trying to get my head around the security. I am still reading and learning, but I came to a point where I got a working version of WCF with Certificate authentication. I know that the code has some weaknesses; however, my initial goal was to create communication using certificate authentication. Also, I wanted to create everything programmatically (no Web.config configurations for the services or clients). The reason for this is that the client should be able to link an Assembly (Class Library) and get access to the server. Also, I am loading the certificates from the file system (again, I know this is not secure). I would like to get a little bit feedback.
The following client snippet is creating an object that I can use to connect to the server. The anonymous type T is my service interface e.g. IService.
Here is my client implementation:
var url = "URL TO WS";
var binding = new WSHttpBinding
{
Security =
{
Mode = SecurityMode.Message,
Message = {ClientCredentialType = MessageCredentialType.Certificate}
}
};
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
if (channelFactory.Credentials != null)
{
channelFactory.Credentials.ClientCertificate.Certificate =
new X509Certificate2(#"PATH\TO\Client.pfx"); // Client Certificate PRIVATE & PUBLIC Key
channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None; // I know this is not good, but I dont have a valid certificate from a trusted entity
}
wcfClient = channelFactory.CreateChannel();
return wcfClient;
The service is a bit more complex. I use .svc files with their code-behind. If I understand the use of .svc files correctly, then I believe this is the entry point where the .NET framework creates a ServiceHost and automatically opens it? In my implementation I do not open the ServiceHost, I only implemented a ServiceHostFactoryBase and referenced it in the .svc Markup language. Look at the Factory section - this is the part where I implement my custom Host Factory.
<%# ServiceHost Language="C#" Debug="true"
Service="Service.Services.LevelService" CodeBehind="LevelService.svc.cs"
Factory="Service.Security.ServiceHostFactory.HostFactory" %>
And my custom Host Factory looks like this:
public class HostFactory : ServiceHostFactoryBase
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
var serviceType = Type.GetType(constructorString);
if (serviceType.GetInterfaces().Count() != 1)
throw new NotImplementedException("The service can only have one implemented interface");
var interfaceType = serviceType.GetInterfaces()[0];
var myServiceHost = new ServiceHost(serviceType, baseAddresses);
var httpBinding = new WSHttpBinding();
httpBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
httpBinding.Security.Mode = SecurityMode.Message;
myServiceHost.Credentials.ServiceCertificate.Certificate = new X509Certificate2(#"PATH\TO\Server.pfx");
myServiceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
myServiceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = new MyX509CertificateValidator();
myServiceHost.Credentials.ClientCertificate.Certificate = new X509Certificate2(#"PATH\TO\Client.cer");
myServiceHost.AddServiceEndpoint(interfaceType, httpBinding, String.Empty);
return myServiceHost;
}
}
The custom validator doess't do much yet, but here it is as well:
public class MyX509CertificateValidator : X509CertificateValidator
{
public override void Validate(X509Certificate2 certificate)
{
// Check that there is a certificate.
if (certificate == null)
{
throw new ArgumentNullException("certificate");
}
// Check that the certificate issuer matches the configured issuer.
//throw new SecurityTokenValidationException("Certificate was not issued by a trusted issuer");
}
}
If I understand correctly, the Server has ONLY the PUBLIC key of the client registered since I only reference the .cer file.
My big question is now, if I would like to get anything like this on a production server - and lets assume nobody will actually get the executables (including the certificates), would this be a possible solution to keep unwanted people out of my webservice? Basically, I don't want anybody else consuming my webservice - only if you have the proper certificate. Also, how much of an issue is the part where I set on the client:
CertificateValidationMode = X509CertificateValidationMode.None
I know there are many questions - but overall, I would like to know if I made some fundamental mistakes in this implementation.
Ok,
after going through a lot of tutorials and demo applications, I figured out that the best way to go ahead is actually using the Certificate Store on Windows. However, I still might consider a hybrid solution where the Server has the certificates in the Certificate store and the client has it embedded in a resource. If you are struggling with WCF and Certificates, have a look at those links:
IIS7 Permissions Overview - ApplicationPoolIdentity
I was able to create Transport as well as Message secured WCF web services. I would suggest to READ the linked articles because there is so much information that will make you understand certificates and their usage. Especially when dealing with self-singed certificates!
I ended up implementing wsHttpBinding using Message Security Mode + Client Certificate with ChainTrust.
Hope this will help someone else!
I need to dynamically set the endpoint for my Magento implementation using C# but can't override C#'s default check of the endpoint path and credentials in the web.config.
Does anyone know how to do this?
My service currently looks like this:
using (Mage_Api_Model_Server_V2_HandlerPortTypeClient proxy = new Mage_Api_Model_Server_V2_HandlerPortTypeClient("NameOfEndpoint", ConnectionCurrent.WsdlPath))
{
string sessionKey = proxy.startSession();
string loginSession = proxy.login(ConnectionCurrent.UserName, ConnectionCurrent.Password);
...
At Login, it then says that I have two endpoints configured.
I've looked everywhere but can't find a solution.
Thanks!!
This is using WCF but it is similarly done with the older web services implementation:
EndpointAddress endPoint = new EndpointAddress("http://some.endpoint.addr");
Binding binding = new WSHttpBinding(SecurityMode.None);
var service = new Mage_Api_Model_Server_V2_HandlerPortTypeClient(binding, endpoint);
Some background:
in order to provide authentication I'm using certificates on client and server side (WCF) and use one certificate for all clients (manually loading it from application directory - not the safest way, but it doesn't require to manage certificate storage and making installation more difficult):
AddressHeader hostHdr = AddressHeader.CreateAddressHeader(ServiceFactory.CLIENT_HOST_HEADER, ServiceFactory.NAMESPACE, hostName);
builder.Headers.Add(hostHdr);
builder.Identity = new X509CertificateEndpointIdentity(GetServiceCertificate(name));
_factory = new ChannelFactory<T>(name, builder.ToEndpointAddress());
_factory.Credentials.ClientCertificate.Certificate = GetClientCertificate(name);
X509ServiceCertificateAuthentication auth = _factory.Credentials.ServiceCertificate.Authentication;
auth.CertificateValidationMode =X509CertificateValidationMode.Custom;
auth.CustomCertificateValidator = new CustomCertificateValidator(new[] {GetServiceCertificate(name)});
This is client side, and serverside host setting up looks like this:
private void CertificateSetup(ServiceHost host)
{
if (ServiceCertificate != null)
host.Credentials.ServiceCertificate.Certificate = ServiceCertificate;
X509ClientCertificateAuthentication authentication =
host.Credentials.ClientCertificate.Authentication;
authentication.CertificateValidationMode =
X509CertificateValidationMode.Custom;
authentication.CustomCertificateValidator =
new CustomCertificateValidator(new [] {ClientCertificate});
}
That works fine and allows to sign messages, but as far as security mode set in following way:
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
But i need
string name = OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Name;
somehow to obtain WindowsIdentity in ServiceSecurityContext.
Mixed (Transport and Message) security mode is not helpful, because I don't know why but even if i set Windows clientCredentials in config for Transport part mode infrastructure tries to establish SSL connection.
Any ideas ????
Certificates are used for message signing,i.e. proving that other side is either real client or service (man-in-the-middle). But authorization in system being developed partially relies on WindowsIdentity in ServiceSecurityContect. All i want-include WindowsIdentity in sec.context, meanwhile PrimaryIdentity is X509CertIdentity.
So i need to know on serverside which domain user requested service operation.
Try this link re: Impersonation:
http://msdn.microsoft.com/en-us/library/ms730088.aspx
section on - "Mapping a Client Certificate to a Windows Account", seems like what you're after.
with config on client:
authentication mapClientCertificateToWindowsAccount="true"
Client code:
' Create a binding that sets a certificate as the client credential type.
Dim b As WSHttpBinding = New WSHttpBinding()
b.Security.Message.ClientCredentialType = MessageCredentialType.Certificate
' Create a service host that maps the certificate to a Windows account.
Dim httpUri As Uri = New Uri("http://localhost/Calculator")
Dim sh As ServiceHost = New ServiceHost(GetType(HelloService), httpUri)
sh.Credentials.ClientCertificate.Authentication.MapClientCertificateToWindowsAccount = True
Hope that helps
If you're using message security with certificates at each end securing it, I'm not quite sure why you would need a windows identity? What's the reasoning behind that?
Maybe this link will be of use - sections 6 & 7 have details of config settings for certificate authentiaction, which works similarly to SSL:
http://www.codeproject.com/KB/WCF/wcf_certificates.aspx