Programmatically create a secure web http rest service - c#

I would like to programmatically create a secure webhttp REST service in a self host environment i.e in a Console Application but i cannot find any tutorials which allow me to do this.
However i have created secure webhttp rest service using the WCF service library project without any C# code. I used netsh command to insert the certficiate that i had made and updated my config appropriately.
Can anyone advise please?

Just a quick update, I got it working but only http side as of yet.
Here is my service side code:
WebServiceHost serviceHost = new WebServiceHost(typeof(ProductServiceTest), new Uri("http://localhost:9000/"));
WebHttpBinding webHttpBinding = new WebHttpBinding();
webHttpBinding.MaxReceivedMessageSize = 65536 * 2;
webHttpBinding.MaxBufferPoolSize=2147483647L;
webHttpBinding.MaxBufferSize=2147483647;
webHttpBinding.MaxReceivedMessageSize = 2147483647L;
serviceHost.AddServiceEndpoint(typeof(IProductServiceTest), webHttpBinding, "");
// Check to see if the service host already has a ServiceMetadataBehavior
ServiceMetadataBehavior smb = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
// If not, add one
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
serviceHost.Description.Behaviors.Add(smb);
serviceHost.Open();
Console.WriteLine("Press <ENTER> to terminate the service host");
Console.ReadLine();
serviceHost.Close();
I created a console app for the client side and got my client side working by doing this:
ChannelFactory<IProductServiceTest> cf = new ChannelFactory<IProductServiceTest>(new WebHttpBinding(), "http://localhost:9000");
cf.Endpoint.Behaviors.Add(new WebHttpBehavior());
IProductServiceTest channel = cf.CreateChannel();
var test = channel.GetProductData("8");
Console.WriteLine(test.ProductDescription);
Console.Read();

Related

Converting a WCF service to console application

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
}
}

How to set different credentials of c# wcf client for proxy and service endpoint?

Can somebody tell me how, in a generated wcf client, to set separate credentials for the network proxy and the actual service endpoint? So I have a simlar situation as in How to set proxy credentials to specific wcf client?, but I need to authenticate differently to the proxy than to the web service endpoint:
var b = client.Endpoint.Binding as BasicHttpBinding;
/// [...]
b.Security.Mode = BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; // !!!
b.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic; // !!!
client.ClientCredentials.UserName.UserName = login;
client.ClientCredentials.UserName.Password = password;
So where to put my proxyLogin and proxyPassword? Any ideas?

One WCF service on two addresses

My situation:
I have one WCF service on address http://0.0.0.0:10004/Service.svc. Using Interface_1, Service_1_Class, ServiceHost_1_Class.
Now I need second version of this service with authorisation. Choosing address https://0.0.0.0:10004/Service2.svc, using Interface_1, Service_1_Class, ServiceHost_2_Class.
Got the error
HTTP could not register URL https://+:10004/Service2.svc/. Another application has already registered this URL with HTTP.SYS.
If copy-paste and use Interface_2, Service_2_Class, ServiceHost_2_Class (same classes with different name) everything works.
Not the big problem, but want to know: how to bind same service class on two addresses? So which step may I miss? I.e. binding one to one works, binding one to two - not, where is difference? If there should not be differences (i.e. problem in code), I'll close this question.
PS: sorry, cant post code: too big, too complicated, not clear. Question not about code, but conception
As suggested, you need to setup multiple service endpoints each with a different binding. One binding with no security enabled and the other binding with security enabled.
Here is a working example of how to host the same service at two different endpoints. One endpoint has no security at all and the other endpoint provides HTTP-based client authentication.
var baseUri = new Uri("http://localhost:10004/");
var serviceHost = new ServiceHost(typeof(HelloWorldService), baseUri);
try
{
var unsecureServiceEndpoint = serviceHost.AddServiceEndpoint(typeof(IHelloWorldService), new WebHttpBinding(), "Service.svc");
unsecureServiceEndpoint.Behaviors.Add(new WebHttpBehavior());
unsecureServiceEndpoint.Name = "UnsecureEndpoint";
var secureBinding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
secureBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var secureServiceEndpoint = serviceHost.AddServiceEndpoint(typeof(IHelloWorldService), secureBinding, "Service2.svc");
secureServiceEndpoint.Behaviors.Add(new WebHttpBehavior());
secureServiceEndpoint.Name = "SecureEndpoint";
serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
serviceHost.Open();
Console.WriteLine("Hosting - {0} # {1}", unsecureServiceEndpoint.Name, unsecureServiceEndpoint.Address);
Console.WriteLine("Hosting - {0} # {1}", secureServiceEndpoint.Name, secureServiceEndpoint.Address);
Console.WriteLine("Press <Enter> to stop the services.");
Console.ReadLine();
serviceHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
serviceHost.Abort();
}

no connection could be made because the target machine actively refused it 127.0.0.1:8000 inner exception

I am new to WCF and have created one solution with 2 projects, one is the client app, the other is the web service.
When I compile the web service code it runs without error:
namespace Microsoft.ServiceModel.Samples
{
class Program
{
static void Main(string[] args)
{
// Step 1 of the address configuration procedure: Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8888/ServiceModelSamples/Service");
/* Step 2 of the hosting procedure: Create ServiceHost
* Use the ServiceHost class to configure and expose a service for use by client applications when you are not using Internet Information Services (IIS) to expose a service.
* IIS interacts with a ServiceHost object on your behalf.
*/
ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress);
try
{
//WSHttpBinding is an interoperable binding that supports distributed transactions and secure, reliable sessions.
WSHttpBinding ws = new WSHttpBinding();
ws.Security.Mode = SecurityMode.Message; //Use SOAP message security
ws.Security.Message.ClientCredentialType = MessageCredentialType.Windows; //Use windows authentication
// Step 3 of the hosting procedure: Add a service endpoint.
// Adds a service endpoint to the hosted service with a specified contract, binding, and endpoint address.
// The contract is the definition of what functionality the web service offers (i.e. its API)
// The binding specifies how the service communicates (protocols, transports, and message encoders)
// The address is the name of the endpoint being added to this service host
selfHost.AddServiceEndpoint(
typeof(ICalculator),
ws,
"CalculatorService");
// Step 4 of the hosting procedure: Enable metadata exchange.
// Controls the publication of service metadata and associated information.
// HttpGetEnabled indicates whether to publish service metadata for retrieval using an HTTP/GET request.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
//add the service metadata behavior to the list of service host behaviors
selfHost.Description.Behaviors.Add(smb);
// Step 5 of the hosting procedure: Start (and then stop) the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
Console.ReadLine();
}
}
On the client side, the code fails on the client.add line with "There was no endpoint listening at http://localhost:8000/ServiceModelSamples/Service that could accept the message", with the inner exception of "no connection could be made because the target machine actively refused it 127.0.0.1:8000"
//Step 1: Create an endpoint address and an instance of the WCF Client.
CalculatorClient client = new CalculatorClient();
// Step 2: Call the service operations.
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
My app.config looks like:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ICalculator" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8000/ServiceModelSamples/Service"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICalculator"
contract="CalculatorServiceReference.ICalculator" name="WSHttpBinding_ICalculator">
</endpoint>
</client>
</system.serviceModel>
</configuration>
Can an expert point me in the right direction? I have tried many different endpoint addresses, but no luck. Thanks in advance!
Your server is being hosted on 8888 while your client is attempting to connect to port 8000. Change the server or client port so that they match.
It looks like you want it to be 8000, not 8888.
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
I tested your code. I believe you need to add the service to your base address in your client app.config file like this
endpoint address="http://localhost:8888/ServiceModelSamples/Service/CalculatorService"

Difference in authentication of WCF Channel Factory vs Service Reference

I am consuming a 3rd party https web service in a WCF service using the 2 ways described below.
Using Service Reference
ServiceClient client=new ServiceClient();
client.ClientCredentials.UserName.UserName ="xxx";
client.ClientCredentials.UserName.Password = "pwd";
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
ServiceResponse response=client.GetData();
2.Using channel factory
ChannelFactory<IService> client = new ChannelFactory<IService>(binding, address);
var proxy = client.CreateChannel();
client.Credentials.UserName.UserName ="xxx";
client.Credentials.UserName.Password ="pwd";
ServiceResponse response=client.GetData();
I am able to pass the security credentials using the first approach and i am able to get the respone back from the 3rd party web service.But I am unable to get the response when i use the second approach. I can see that the username,password are added in the security header of the outoing SOAP message with the first approach but not with the second approach.I would be glad if some one can throw some suggestion here about the channel factory approach.
The issue is when you're assigning the credentials - in your current code, you're creating the proxy after you create the factory, and then you assign the credentials to the factory. But that has no effect on the created channel:
ChannelFactory<IService> client = new ChannelFactory<IService>(binding, address);
var proxy = client.CreateChannel();
client.Credentials.UserName.UserName ="xxx";
client.Credentials.UserName.Password ="pwd";
var proxy is a an implementation of IChannel - setting the credentials on the factory (client) has no effect on the already created channels - just the ones created later.
Try setting the credentials and then calling CreateChannel():
ChannelFactory<IService> client = new ChannelFactory<IService>(binding, address);
client.Credentials.UserName.UserName ="xxx";
client.Credentials.UserName.Password ="pwd";
var proxy = client.CreateChannel();
ServiceResponse response=client.GetData();

Categories