I have a selfhosted WCF service publishing a REST web API. The service is configured programmatically, and currently is correctly working via HTTP.
Now I need to make it work over HTTPS, with an authentication based on certificate file.
I know the suggested way to do this is installing the certificate in the Windows Certificate Store on the server machine, but this way is not possible in my circumstances.
I need to load the certificate from the file.
After some resarches, I wrote my code, but when I try accessing the web service, a System.ServiceModel.CommunicationException is thrown, with the message:
An error occurred while making the HTTP request to ... 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.
Here's my code for the server side:
_host = new WebServiceHost(_hostedService, Uri);
//Configuring the webHttpBinding settings
var httpBinding = new WebHttpBinding();
httpBinding.Security.Mode = WebHttpSecurityMode.Transport;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
httpBinding.SendTimeout = new TimeSpan(0, 5, 0);
httpBinding.MaxBufferSize = 2147483647;
httpBinding.MaxBufferPoolSize = 2147483647;
httpBinding.MaxReceivedMessageSize = 2147483647;
httpBinding.ReaderQuotas.MaxDepth = 2147483647;
httpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
httpBinding.ReaderQuotas.MaxArrayLength = 2147483647;
httpBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
httpBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;
//Add the endpoint with the webHttpBinding settings to the WebServiceHost configuration
_host.AddServiceEndpoint(typeof(IService), httpBinding, Uri);
ServiceDebugBehavior stp = _host.Description.Behaviors.Find<ServiceDebugBehavior>();
stp.HttpHelpPageEnabled = false;
ServiceBehaviorAttribute sba = _host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
sba.InstanceContextMode = InstanceContextMode.Single;
ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpsGetEnabled = true };
_host.Description.Behaviors.Add(smb);
X509Certificate2 trustedCertificate = new X509Certificate2("certificate.pfx", "password");
_host.Credentials.ServiceCertificate.Certificate = trustedCertificate;
_host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
Here's my code for the client side:
var httpBinding = new WebHttpBinding();
httpBinding.Security.Mode = WebHttpSecurityMode.Transport;
httpBinding.Security.Transport = new HttpTransportSecurity() { ClientCredentialType = HttpClientCredentialType.Certificate };
var httpUri = new Uri(String.Format("https://{0}:{1}", ipAddress, tcpPort));
var httpEndpoint = new EndpointAddress(httpUri);
var newFactory = new ChannelFactory<IService>(httpBinding, httpEndpoint);
newFactory.Endpoint.Behaviors.Add(new WebHttpBehavior());
X509Certificate2 trustedCertificate = new X509Certificate2("certificate.pfx", "password"); //SSL
newFactory.Credentials.ClientCertificate.Certificate = trustedCertificate;
var channel = newFactory.CreateChannel();
var response = channel.Ping("helo");
The Exception is thrown on the last line (channel.Ping("helo")).
I need to make it work WITHOUT installing the certificate on the server machine.
Thank you very much.
Bye.
As far as I know, when we host the self-hosted WCF service over Https, whatever way we use (load certificate file or configure the certificate via Windows Certificate Store), it is impossible to make the service works normally. The only way we need to do is binding the certificate manually by using the following command.
netsh http add sslcert ipport=0.0.0.0:8000 certhash=0000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
Here is official document, wish it is useful to you.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
https://learn.microsoft.com/en-us/windows/desktop/http/add-sslcert
Here are some examples were ever written by me.
WCF Service over HTTPS without IIS, with SSL Certificate from CERT and KEY strings or files
Chrome (v71) ERR_CONNECTION_RESET on Self Signed localhost on Windows 8 Embedded
Feel free to let know if there is anything I can help with.
Related
I have added a Java WSDL in my project as Web Reference. I am consuming it to call a service on an endpoint. I have added a WebMethod in an ASMX file and calling the service there. The requirement is to use WSE Security and sign the request with X509 Certificate.
Unfortunately, the Timestamp is creating issues and I am getting the response " An error was discovered processing the header". The Same request works from SoapUI if I remove the TimeStamp element from it.
This is how the request look like
Here is the code which creates the Security elements
//Set WSSE Security
svc.RequestSoapContext.Security.Timestamp.TtlInSeconds = 300;
svc.RequestSoapContext.Security.MustUnderstand = true;
svc.RequestSoapContext.Security.Tokens.Add(newtoken);
Microsoft.Web.Services3.Security.MessageSignature signature = new Microsoft.Web.Services3.Security.MessageSignature(newtoken);
signature.SignatureOptions = Microsoft.Web.Services3.Security.SignatureOptions.IncludeSoapBody;
svc.RequestSoapContext.Security.Elements.Add(signature);
===============
USING WCF
The problem persists even if I do it using WCF. As soon as I add IncludeTimestamp = false; the request is not getting sent and setting it to true is able to create request.
Here is the WCF Code -
//Create Endpoint
EndpointAddress address = new EndpointAddress((istest == true ? CHORUS_UFB_EMMA : CHORUS_UFB_PROD));
//Add Certificate to EndPoint Service
X509Certificate2 cert = new X509Certificate2(#"Certificate Path", "Password", X509KeyStorageFlags.PersistKeySet);
//Setup custom binding with HTTPS + Body Signing + Soap1.1
CustomBinding binding = new CustomBinding();
//HTTPS Transport
HttpsTransportBindingElement transport = new HttpsTransportBindingElement();
//Set Security Binding as Transport
TransportSecurityBindingElement tsec = SecurityBindingElement.CreateCertificateOverTransportBindingElement(MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
tsec.EnableUnsecuredResponse = true;
tsec.AllowInsecureTransport = true;
tsec.SecurityHeaderLayout = SecurityHeaderLayout.Lax;
tsec.LocalServiceSettings.DetectReplays = false;
tsec.LocalClientSettings.DetectReplays = false;
tsec.IncludeTimestamp = false;
tsec.SetKeyDerivation(false);
tsec.EndpointSupportingTokenParameters.Signed.Add(new SecureConversationSecurityTokenParameters());
//Setup for SOAP 11 and UTF8 Encoding
TextMessageEncodingBindingElement textMessageEncoding = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
//Bind in order (Security layer, message layer, transport layer)
binding.Elements.Add(tsec);
binding.Elements.Add(textMessageEncoding);
binding.Elements.Add(transport);
Here is the generated request using above code
Any help on this would be appreciated.
This could potentially be caused by time differences between your client and the web server where the service you are calling is hosted.
Double check if the time on both servers match and are in sync. Times might need to be within 5 minute window.
I have a soap web service (sap me web service), I generated the wcf proxy. no problem, I developed a WinForms application no problem
Now i try to use it in my wcf service (webHttpBinding binding in domain network) but i have an authentication error:
WCF - The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="mySoapServiceName"
It is IIS User problem ?
Thanks
SAP is using basic auth. You need to specify the username and password after you have created the proxy, for example:
proxy.Credentials.UserName.UserName = "joe";
proxy.Credentials.UserName.Password = "doe";
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
EndpointAddress ea = new EndpointAddress("url de mon web service soap");
SfcServices.SFCProcessingInClient myClient = new SfcServices.SFCProcessingInClient(myBinding, ea);
myClient.ClientCredentials.UserName.UserName = _MESwsLogin;
myClient.ClientCredentials.UserName.Password = _MESwsPassword;
SfcServices.SFCStateRequestMessage_sync srm = new SfcServices.SFCStateRequestMessage_sync();
SfcServices.SFCStateRequest sr = new SfcServices.SFCStateRequest();
srm.SfcStateRequest = sr;
sr.SiteRef = new SfcServices.SiteRef();
sr.SiteRef.Site = _MESsite;
sr.SfcRef = new SfcServices.SFCRef();
sr.SfcRef.Sfc = "12345678903";
sr.includeSFCStepList = true;
SfcServices.SFCStateConfirmationMessage_sync response = myClient.FindStateByBasicData(srm);
strOrdreFab = response.SfcStateResponse.SFCStatus.Sfc.ShopOrderRef.ShopOrder;
strCodeProduit = response.SfcStateResponse.SFCStatus.Sfc.ItemRef.Item;
strIndice = response.SfcStateResponse.SFCStatus.Sfc.ItemRef.Revision;
I'm trying to get my generated WCF ServiceClient to send its requests to the WCF service through a HTTP (not HTTPS) proxy with username/password authentication, however I just can't get it work. My WCF service uses basicHttpBinding so I tried to configure my ServiceClient instance like so:
var svc = new ServiceClient();
var b = svc.Endpoint.Binding as BasicHttpBinding;
b.ProxyAddress = new Uri(proxyAddress);
b.UseDefaultWebProxy = false;
b.Security.Mode = BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
b.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
svc.ClientCredentials.UserName.UserName = proxyUsername;
svc.ClientCredentials.UserName.Password = proxyPassword;
This however results in a System.ArgumentException saying:
The provided URI scheme http is invalid. expected https
When I set the b.Security.Mode to BasicHttpSecurityMode.None though, it seems the HTTP proxy settings are ignored by WCF altogether!
The second solution I tried was to set the DefaultWebProxy property of WebRequest and set the UseDefaultWebProxy property to true such as so:
var webProxy = new WebProxy(proxyAddress, true);
webProxy.Credentials = new NetworkCredential(proxyUsername, proxyPassword);
WebRequest.DefaultWebProxy = webProxy;
var svc = new ServiceClient();
var b = svc.Endpoint.Binding as BasicHttpBinding;
b.UseDefaultWebProxy = true;
However this also doesn't work and ServiceClient doesn't send it's requests through the HTTP proxy.
I'm out of ideas here so please let me know what I am doing wrong, thank you!
Set the security mode to BasicHttpSecurityMode.TransportCredentialOnly. This allows for passing plain-text authentication details over HTTP.
I am implementing mutual handshake over https using wcf, and I receive an error:
"The HTTP request was forbidden with client authentication scheme 'Anonymous'."
Service code:
var binding = new BasicHttpBinding()
{
Security =
{
Mode = BasicHttpSecurityMode.Transport,
Transport = { ClientCredentialType = HttpClientCredentialType.Certificate },
},
};
var sh = new ServiceHost(typeof(EchoService), new Uri("https://localhost:9876"));
//sh.Description.Behaviors.Add(new ServiceMetadataBehavior());
//sh.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpsBinding(), "mex");
sh.AddServiceEndpoint(typeof(IEchoService), binding, "");
sh.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
sh.Open();
Client code:
var binding = new BasicHttpBinding()
{
Security =
{
Mode = BasicHttpSecurityMode.Transport,
Transport = { ClientCredentialType = HttpClientCredentialType.Certificate },
},
};
var sslClientFactory = new ChannelFactory<IEchoService>(binding, "https://localhost:9876");
sslClientFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
var sslClient = sslClientFactory.CreateChannel();
var response = sslClient.Echo("Https Echo");
I have assign this certificate to port using httpcfg.
If I change binding from BasicHttpBinding to NetTcpBinding it works fine.
If I run two instances of my service (in one process), one which uses NetTcpBinding and second one which uses BasicHttpBinding, and consume it from net tcp client and https client, both works fine (clients use the same certificate).
What cases that if i run only my https client I get "The HTTP request was forbidden with client authentication scheme 'Anonymous'."?
I have noticed that I have a lot of doubled certs (pairs of two identically certs - with the same thumbrpint) in store (I dont know how i put them there like that), and I think that this was a problem. Maybe when certs are doubled in store, server sends to client only those issuers from not doubled certs?
I would like to setup a module which will communicate with other modules remotely acting both as a service and as a client. The communcation should go in SOAP 1.2 and it should use OASIS WSS 1.1, and X.509 certificate token profile.
OASIS WSS 1.1 X.509 specs
I already made a development certificate using makecert and it is trusted already.
Since the module is essentially C# based, all settings are given in code. So far I got the following service code:
The code for the binding:
System.ServiceModel.Channels.AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement();
asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
asbe.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient };
asbe.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient };
asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
asbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
asbe.EnableUnsecuredResponse = true;
asbe.IncludeTimestamp = false;
asbe.SetKeyDerivation(false);
asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15;
asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
CustomBinding myBinding = new CustomBinding();
myBinding.Elements.Add(asbe);
myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8));
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.RequireClientCertificate = true;
myBinding.Elements.Add(httpsBindingElement);
The code for the behaviour:
//Then initiate the service host
_Host = new ServiceHost(typeof(TClass), baseAddress);
//Add the service endpoint we defined
_Host.AddServiceEndpoint(typeof(TInterface), _Binding, typeof(TInterface).ToString());//BindingHelper.GetUserNameBinding(), "");
//Set searching the certificate
_Host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyServerCert");
_Host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
_Host.Credentials.ClientCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
//Allow the metadata spreading
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpsGetEnabled = true;
smb.HttpGetEnabled = true;
_Host.Description.Behaviors.Add(smb);
ServiceDebugBehavior sdb = new ServiceDebugBehavior();
sdb.IncludeExceptionDetailInFaults = false; //Should only provide the endpoint property (GP WS-Message profile specs)
//Add the appropriate endpoint
if (baseAddress.AbsoluteUri.Contains("https"))
_Host.AddServiceEndpoint(
typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpsBinding(),
"mex");
else
_Host.AddServiceEndpoint(
typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex");
On the client side I use the same code to create the binding, plus I use the following behavior:
channelFactory = new ChannelFactory<T>(bindIn, serviceAddress);
if (wsFeature != null)
{
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Endpoint.Behaviors.Add(wsFeature);
channelFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyServerCert");
channelFactory.Credentials.ServiceCertificate.SetScopedCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyServerCert",serviceAddress.Uri);
}
_ProxiObject = channelFactory.CreateChannel();
Here basically the behavior wsFeature is a simple class doing practically nothing (just implements blank functions for IEndpointBehavior).
I have both service and client on the same machine at https://localhost.:8084/testhosting4, and even though both service and client are successfully created I only got the famous "An error occurred while making the HTTP request to https://localhost.:8084/testhosting4. etc." error.
I already managed to connect via an unsecure channel with the module (BasicHttpBinding - no security) and exchange messages, so I am sure that I make a mistake in defining the binding or assigning credentials. Obviously, I browsed a lot here already, but couldn't come up with a working solution.
This is the first time I meet WCF and X509 and I'm not in secure communications at all either. So plenty of occasions to make a mistake. Please point out those which I've made.
Thank you!