I have a WCF service app and client app that I have written. I have been using BasicHTTPBinding on both ends.
To be clear: This is NOT a web application. The service is self-hosted in a PC application (NOT IIS). All configuration of the endpoints on both the client and server are done in code, not in app.config. In fact, to make sure, I have commented out the entire System.Servicemodel sections of app.config for both the client and server applications, and everything works great. This is all using BasicHTTPBinding.
Now, I want to change everything to use HTTPS instead of HTTP, for security reasons. I have changed what I thought I needed to change in my code, but it's not working.
Original Code (This works fine!)
Server:
BasicHttpBinding _binding;
Service1 _generalService;
ServiceHost _generalHost;
_binding = new BasicHttpBinding();
_binding.MaxReceivedMessageSize = 1000000;
_generalService = new Service1(_dbConnParams, _imagePath, _registrationProvider, LicenseCount);
_generalHost = new ServiceHost(_generalService, new Uri("http://localhost:8000"));
_generalHost.AddServiceEndpoint(typeof(IService1), _binding, "Service1");
_generalHost.Open();
Client:
internal static ToOrson.IService1 SetupService(string ServerAddress, TimeSpan? timeout = null)
{
var myBinding = new BasicHttpBinding();
myBinding.MaxReceivedMessageSize = 1000000;
if (timeout != null)
myBinding.SendTimeout = (TimeSpan)timeout;
string fullAddress = string.Format("http://{0}:8000/Service1", ServerAddress);
var myEndpoint = new EndpointAddress(fullAddress);
var myChannelFactory = new ChannelFactory<ToOrson.IService1>(myBinding, myEndpoint);
return myChannelFactory.CreateChannel();
}
private async void TestButton_Click(object sender, RoutedEventArgs e)
{
var TestService = MainWindow.SetupService(ServerBox.Text);
try
{
await TestService.TestConnectionAsync();
}
catch (Exception connException)
{
MessageBox.Show("Connection failed: " + connException.Message, "Error");
return;
}
MessageBox.Show("Connection succeeded!", "Success");
}
Code modified for HTTPS (This does NOT work)
Server:
BasicHttpsBinding _binding;
Service1 _generalService;
ServiceHost _generalHost;
_binding = new BasicHttpsBinding();
_binding.MaxReceivedMessageSize = 1000000;
_generalService = new Service1(_dbConnParams, _imagePath, _registrationProvider, LicenseCount);
_generalHost = new ServiceHost(_generalService, new Uri("https://localhost:8000"));
_generalHost.AddServiceEndpoint(typeof(IService1), _binding, "Service1");
_generalHost.Open();
Client:
internal static ToOrson.IService1 SetupService(string ServerAddress, TimeSpan? timeout = null)
{
var myBinding = new BasicHttpsBinding();
myBinding.MaxReceivedMessageSize = 1000000;
if (timeout != null)
myBinding.SendTimeout = (TimeSpan)timeout;
string fullAddress = string.Format("https://{0}:8000/Service1", ServerAddress);
var myEndpoint = new EndpointAddress(fullAddress);
var myChannelFactory = new ChannelFactory<ToOrson.IService1>(myBinding, myEndpoint);
return myChannelFactory.CreateChannel();
}
private async void TestButton_Click(object sender, RoutedEventArgs e)
{
var TestService = MainWindow.SetupService(ServerBox.Text);
try
{
await TestService.TestConnectionAsync();
}
catch (Exception connException)
{
MessageBox.Show("Connection failed: " + connException.Message, "Error");
return;
}
MessageBox.Show("Connection succeeded!", "Success");
}
Basically, I just changed BasicHTTPBinding to BasicHTTPSBinding and HTTP:// to HTTPS:// in all places in the code.
Here is the exception that I'm getting:
System.ServiceModel.CommunicationException "An error occurred while
making the HTTP request to https://localhost:8000/Service1. This could
be due to the fact that the server certificate is not configured
properly with HTTP.SYS in the HTTPS case. This could also be caused by
a mismatch of the security binding between the client and the server."
This exception is occurring on the client, on the call to TestService.TestConnectionAsync() (which is one of my OperationContracts).
Here are the inner exceptions:
System.Net.WebException "The underlying connection was closed: An
unexpected error occurred on a send."
System.IO.IOException "Unable to read data from the transport
connection: An existing connection was forcibly closed by the remote
host."
System.Net.Sockets.SocketException "An existing connection was
forcibly closed by the remote host"
Yes, I updated my service reference after these changes. Yes, I am targeting the .NET 4.5 framework. I don't think it's a firewall issue, as I'm using the same port (8000) in both cases, and I already have that port open.
What am I doing wrong?
Related
I've researched this for a day now and am turning to you experts here for your advice.
I have REST service that is the main IIS hosted service.
This service needs to interface with a 32-bit unmanaged .dll. In order to do this I'm taking one common approach which is creating a self hosted service and making calls to it.
With that background, I'm having one heck of a time getting the self hosted one working propertly.
here is the code that WORKS:
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/theSvc");
Uri mexUri = new Uri("http://localhost:8080/theSvc/mex");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(ImgService), baseAddress))
{
// var wsHttpBinding = new WSHttpBinding();
// wsHttpBinding.MaxReceivedMessageSize = int.MaxValue;
// host.AddServiceEndpoint(typeof(IImgService), wsHttpBinding, baseAddress);
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetUrl = mexUri;
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
Now...if I uncomment these lines in order to increase the maxreceivedMessageSize and remove the baseAddress from the using statement, I can no longer add a reference to the service:
So change this:
using (ServiceHost host = new ServiceHost(typeof(ImgService), baseAddress))
To this:
using (ServiceHost host = new ServiceHost(typeof(ImgService)))
And uncomment this:
// var wsHttpBinding = new WSHttpBinding();
// wsHttpBinding.MaxReceivedMessageSize = int.MaxValue;
// host.AddServiceEndpoint(typeof(IImgService), wsHttpBinding, baseAddress);
The error that I receive is:
... -> local host
There was an error downloading 'http://...:8080/theSvc/_vti_bin/ListData.svc/$metadata'.
The request failed with HTTP status 400: Bad Request.
Metadata contains a reference that cannot be resolved: 'http://...:8080/theSvc'.
Metadata contains a reference that cannot be resolved: 'http://...:8080/theSvc'.
If the service is defined in the current solution, try building the solution and adding the service reference again.
As like everyone else that posts here...I'm in a bind. Any help really appreciated.
It would appear you are not adding the service endpoint.
This is a function I'm using to startup an http host, however I am hosting it in a windows service. Which is the same as your console application. But IIS might think about it a bit differently.
static Uri scannerSvcBaseAddress;
static BasicHttpBinding scannerBinding;
static ServiceHost scannerHost;
public void startScannerWcfService(long eventID)
{
try
{
db.writeServiceLog(eventID, "STARTUP", "=== Scanner WCF Service Starting ===");
string addressToHostAt = ConfigurationManager.AppSettings["ScannerHostBaseAddress"];
if (addressToHostAt != null && addressToHostAt.Trim() != string.Empty)
{
scannerSvcBaseAddress = new Uri(addressToHostAt);
scannerHost = new ServiceHost(typeof(BarCodeService.ScannerService), scannerSvcBaseAddress);
//Allows publishing of METADATA to developers when self hosted on a remote system
ServiceMetadataBehavior smb = scannerHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior();
}
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
scannerHost.Description.Behaviors.Add(smb);
scannerHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
scannerBinding = new BasicHttpBinding();
scannerBinding.MaxReceivedMessageSize = 2147483647;
scannerBinding.Security.Mode = BasicHttpSecurityMode.None;
scannerBinding.OpenTimeout = new TimeSpan(0, 0, 10);
scannerBinding.CloseTimeout = new TimeSpan(0, 0, 10);
scannerBinding.SendTimeout = new TimeSpan(0, 5, 0);
scannerBinding.ReceiveTimeout = new TimeSpan(0, Global.scanUserIdleLogout * 2, 0);
//scannerBinding.ReliableSession.InactivityTimeout = new TimeSpan(2, 0, 0, 0);
scannerHost.AddServiceEndpoint(typeof(BarCodeService.IScannerService), scannerBinding, "");
var behavior = scannerHost.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
scannerHost.Open();
db.writeServiceLog(eventID, "STARTUP", "=== Scanner WCF Service Started ===");
}
else
throw new Exception("Host Base Address not provided");
}
catch (Exception ex)
{
db.writeServiceLog(eventID, "STARTUP", string.Format("Error in ServiceManager.startScannerWcfService: {0} {1}", ex.Message, ex.StackTrace));
throw;
}
}
All that said, as was mentioned above if you only need to expose a string WCF may be overkill. I'm using WCF in a windows services to connect to handheld scanners in a warehouse environment, and I am really happy with how it performs. Though it was a huge pain to get it working initially.
But since the question was asked in the context of WCF I thought I would respond with a known to work function that does essentially what you want to do.
I need to develop a WCF Hosted in a console app WebService.
I made it work using the Mutual Certificate (service and client) method using SecurityMode.Message.
But now i need to change the Security Mode to SecurityMode.Transport and use the wsHttpBinding with SSL. I made this code to host the service but i cannot get the wsdl with the browser, or execute some webmethod in the console app client.
static void Main()
{
var httpsUri = new Uri("https://localhost:8089/HelloServer");
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var host = new ServiceHost(typeof(WcfFederationServer.HelloWorld), httpsUri);
host.AddServiceEndpoint(typeof(WcfFederationServer.IHelloWorld), binding, "", httpsUri);
var mex = new ServiceMetadataBehavior();
mex.HttpsGetEnabled = true;
host.Description.Behaviors.Add(mex);
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The service is up, but i cannot get nothing on the https://localhost:8089/HelloServer.
On fiddler the get request via browser shows me this message:
fiddler.network.https> HTTPS handshake to localhost failed. System.IO.IOException
What im missing here?
Thanks
EDIT:
The Console Application Client Code
static void Main()
{
try
{
var client = new HelloWorldHttps.HelloWorldClient();
client.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPeople,
X509FindType.FindBySubjectName,
"www.client.com");
Console.WriteLine(client.GetData());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
Getting this error:
Could not establish trust relationship for the SSL/TLS secure channel
When it comes to the service, you need to map the certificate to the specific port as described here
http://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx
As for the client, you need to skip the verification of certificate properties like valid date, the domain by relaxing the certificate acceptance policy. An easiest way would be to accept any certiticate
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true
You can finetune the acceptance callback according to the docs to best fit your needs.
i had created dynamic endpoint in server side and that endpoint used by client side.
Server side Code:
for (int i = 1; i <= 3; i++)
{
host.AddServiceEndpoint(typeof(PokerService.IPlayerService),
new NetTcpBinding(),
#"net.tcp://localhost:5054/player" + i);
}
Client side:
NetTcpBinding binding = new NetTcpBinding(SecurityMode.Message);
binding.Name = "NetTcpBinding_IPlayerService";
binding.Security.Message.ClientCredentialType = MessageCredentialType.IssuedToken;
EndpointAddress myEndpointAdd = new EndpointAddress(new Uri("net.tcp://localhost:5054/player1"),
EndpointIdentity.CreateDnsIdentity("pident.cloudapp.net"));
var PlayerChannelFactory = new DuplexChannelFactory<ClientApplication.PlayerService.IPlayerService>(new PlayerHandler(handler, this), binding, myEndpointAdd);
but it give error in this following line :
Player loggedIn = PlayerServiceProxy.Login("testuser" + Guid.NewGuid().ToString());
The error is:
"The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:59.9870000'."
have anyone idea?
It seems like timeout. Review your client and service timeouts configuration.
Also, try to use svcTraceViewer.exe to review wcf trace
i want to secure a Silverlight App with SSL.
So I try to wrote a proof of concept, where I host two BasicHttpBindings. One with BasicHttpSecurityMode.None and the other with BasicHttpSecurityMode.Transport.
But I not able to get the second one running, The WCFTestClient from VS Tools display this error message
// Error: Cannot obtain Metadata from https://localhost:8081/ If this is
// a Windows (R) Communication Foundation service to which you have
// access, please check that you have enabled metadata publishing at the
// specified address. For help enabling metadata publishing, please
// refer to the MSDN documentation at
// http://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata Exchange
// Error URI: https://localhost:8081/ Metadata contains a reference
// that cannot be resolved: 'https://localhost:8081/'. An error
// occurred while making the HTTP request to https://localhost:8081/.
// This could be due to the fact that the server certificate is not
// configured properly with HTTP.SYS in the HTTPS case. This could also
// be caused by a mismatch of the security binding between the client and
// the server. The underlying connection was closed: An unexpected
// error occurred on a send. Unable to read data from the transport
// connection: An existing connection was forcibly closed by the remote
// host. An existing connection was forcibly closed by the remote
// hostHTTP GET Error URI: https://localhost:8081/ There was an
// error downloading 'https://localhost:8081/'. The underlying
// connection was closed: An unexpected error occurred on a send.
// Unable to read data from the transport connection: An existing
// connection was forcibly closed by the remote host. An existing
// connection was forcibly closed by the remote host
I would be great if some could view over my code, I stuck for two days with this. It need to be done all programmatically.
Thanks a lot.
Almost the whole programm: http://pastebin.com/9j9K43tS
The Endpoints
private static readonly Uri UriBase = new Uri("http://localhost:8080/");
private static readonly Uri UriBaseService = new Uri("http://localhost:8080/Basic");
private static readonly Uri UriSecure = new Uri("https://localhost:8081/");
private static readonly Uri UriSecureService = new Uri("https://localhost:8081/Secure");
This Works
private static void BasicHTTPServer()
{
var binding = new BasicHttpBinding();
binding.Name = "binding1";
binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
binding.Security.Mode = BasicHttpSecurityMode.None;
// Create a ServiceHost for the CalculatorService type and provide the base address.
_serviceHost = new ServiceHost(typeof (ServiceBasic), UriBase);
_serviceHost.AddServiceEndpoint(typeof (IServiceBasic), binding, UriBaseService);
_serviceHost.AddServiceEndpoint(typeof (IPolicyRetriever), new WebHttpBinding(), "")
.Behaviors.Add(new WebHttpBehavior());
var smb = new ServiceMetadataBehavior {HttpGetEnabled = true, HttpGetUrl = UriBase};
_serviceHost.Description.Behaviors.Add(smb);
// Open the ServiceHostBase to create listeners and start listening for messages.
_serviceHost.Open();
Logger.Log(Server.Basic, string.Format("Open at {0} Service: {1}", UriBase, UriBaseService));
}
This doesn't Works
private static void SecureHTTPServer()
{
var binding = new BasicHttpBinding();
// it doesnt matter if I use BasicHttpsBinding or BasicHttpBinding
binding.Name = "binding2";
binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
// Create a ServiceHost for the CalculatorService type and provide the base address.
_serviceHostSecure = new ServiceHost(typeof (ServiceBasic), UriSecure);
_serviceHostSecure.Credentials.ServiceCertificate.Certificate = GetCertificate();
//load a certificate from file
_serviceHostSecure.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
X509CertificateValidationMode.None;
_serviceHostSecure.AddServiceEndpoint(typeof (IServiceBasic), binding, UriSecureService);
var webHttpBinding = new WebHttpBinding(WebHttpSecurityMode.Transport);
webHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
_serviceHostSecure.AddServiceEndpoint(typeof (IPolicyRetriever), webHttpBinding, "")
.Behaviors.Add(new WebHttpBehavior());
var smb = new ServiceMetadataBehavior {HttpsGetEnabled = true, HttpsGetUrl = UriSecure};
_serviceHostSecure.Description.Behaviors.Add(smb);
// Open the ServiceHostBase to create listeners and start listening for messages.
_serviceHostSecure.Open();
Logger.Log(Server.Basic, string.Format("Open at {0} Service: {1}", UriSecure, UriSecureService));
}
I have a WPF C# application hosting a WCF service. I have a routine to start the service, closing it if it already exists. WebServce is a property of type ServiceHost:
public void Start()
{
try
{
var certificate = new X509Certificate2(certpath, "");
String uri = "net.tcp://" + WCFAddress + "/MyService";
Uri baseaddress = new Uri(uri);
if (WebService != null) {
try {
WebService.Close();
} catch (Exception) {
WebService.Abort();
}
}
WebService = new ServiceHost(MessageProvider, baseaddress);
WebService.Credentials.ServiceCertificate.Certificate = certificate;
WebService.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
WebService.AddServiceEndpoint(typeof(IMessageService), binding, baseaddress);
WebService.Open();
}
catch (Exception e)
{
//exception handling
}
}
This works fine on startup. It also works fine if I change the port number (in WCFAddress) and call the routine again. It also works if I change the address on the host computer and call it with the new IP address. However, if I change the IP address to an invalid one, the service goes into a Faulted state with the error:
e = {"A TCP error (10049: The requested address is not valid in its context) occurred while listening on IP Endpoint=192.168.1.4:5000."}
The Close() call in the above doesn't raise an exception.
If I then change the IP address to the correct one and call again, I get the same error, with the same old incorrect address, even though I passed it the correct one. Also this time the Close() call raises an error due to the Faulted state, which results in the Abort() call.
I thought the Abort() call would allow the service to then be recreated? Why is it giving me an error about the old address when I'm trying to create the service with a new one? It's like it's hanging onto the old ServiceEndpoint?
Abort(). Then just initiate a new instance with the new address.. then Open().