I want to create a WCF console self hosted - server side authentication with Certificate - rest service.
I encountered a problem with actually invoking the Service, since i always get in the response 401 Unauthorized.
Since this is a "One-way" authentication, where the service is identifying itself to the client, why would i always get a 401 unautorized response as a client application (as if the client needs to identify itself to the service to access its resources?)
Could anyone help me with pinpointig where i am going wrong, and how to get my client-service communication working finally?
The Simple Service contract:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(UriTemplate = "Students", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
List<Student> GetStudentDetails();
// TODO: Add GetMethod with parameter
[OperationContract]
[WebGet(UriTemplate = "Student/{id}", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
Student GetStudentWithId(string id);
//TODO: add one post method here
[OperationContract]
[WebInvoke(Method="POST", UriTemplate = "Student/New", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
void NewStudent(Stream stream);
//TODO: add one post method here
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "Student/NewS", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
void NewStudentS(Student stream);
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class Student
{
[DataMember]
public int ID
{
get;
set;
}
[DataMember]
public string Name
{
get;
set;
}
}
Service Implementation:
public class Service1 : IService1
{
public List<Student> GetStudentDetails()
{
return new List<Student>() { new Student() { ID = 1, Name = "Goran" } };
}
public Student GetStudentWithId(string id)
{
return new Student() { ID = Int32.Parse(id), Name = "Ticbra RanGo" };
}
public void NewStudent(Stream stream)
{
using(stream)
{
// convert Stream Data to StreamReader
StreamReader reader = new StreamReader(stream);
var dataString = reader.ReadToEnd();
Console.WriteLine(dataString);
}
}
public void NewStudentS(Student student)
{
Console.WriteLine(student.Name);
}
}
My console application running the service:
static void Main(string[] args)
{
Uri httpUrl = new Uri("https://localhost:8080/TestService");
using (WebServiceHost host = new WebServiceHost(typeof(Service1)))
{
// Create the binding.
WSHttpBinding binding = new WSHttpBinding();
binding.Name = "binding1";
binding.Security.Mode = SecurityMode.Transport;
host.AddServiceEndpoint(typeof(IService1), binding, httpUrl/*"rest"*/);
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
//Add host certificate to service wcf for identification
host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
"localhost");
host.Open();
foreach (ServiceEndpoint se in host.Description.Endpoints)
Console.WriteLine("Service is host with endpoint " + se.Address);
Console.WriteLine("Host is running... Press < Enter > key to stop");
Console.ReadLine();
host.Close();
}
//Console.WriteLine("ASP.Net : " + ServiceHostingEnvironment.AspNetCompatibilityEnabled);
Console.WriteLine("Host is running... Press < Enter > key to stop");
Console.ReadLine();
}
Note that i create the certificate root and children through KeyStore Explorer, and placed them approriately in personal and trusted root certificates on windows.

I mapped the server certificate to the port 8080 though CMD.
The clients i was using are SOAPUI, and my manual coded client. Client Code:
WebRequest request = HttpWebRequest.Create(urlTextBox.Text);
var webResponse = request.GetResponse();
using (Stream dataStream = webResponse.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
HttpResonseTextBox.Text = responseFromServer;
}
Best regards and thank you in advance so much
The above code takes the windows authentication as the way to authenticate the client.
Therefore, we should call it on the client-side by providing a windows credential. Besides, this kind of WCF service is not called Rest API, it is called SOAP web service. We usually invoke it by using a client proxy.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
Then setting up windows credential and call the method.
If we want to create a rest service, please take the Webhttpbinding to create the service.
Likewise, we need to bind a certificate to the particular port in order to make the transport layer security available.
Netsh Http command.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
Due to that, the security mode of authenticating the client is
HttpClientCredentialType.None. We needn’t provide windows credentials on the client-side.Feel free to let me know if there is anything I can help with.