I am consuming a web service internal to my company from ASP.NET. I've used svcutil.exe to connect to the service and generate the bindings and classes from the wsdl. I am able to connect to the dev version, which does not require authentication. Now we are adding in security. My new URI uses https but also requires user credentials.
I am very new to WCF, and am trying to determine the way to configure this. From my reading on MSDN, it appears that the way to go is using.
UPDATE:
Here is the most recent code I've been trying. This incorporates feedback from the answer(s):
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="svcBehavior">
<serviceCredentials>
<serviceCertificate storeLocation="CurrentUser"
storeName="My"
x509FindType="FindByThumbprint"
findValue="xx xx xx etc"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="CustomerPaymentProgramSOAPBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://***URL***"
binding="wsHttpBinding" bindingConfiguration="CustomerPaymentProgramSOAPBinding"
contract="CppService.CustomerPaymentProgramService" name="CustomerPaymentProgramService">
</endpoint>
</client>
</system.serviceModel>
Here is the calling code:
using (var svc = new CustomerPaymentProgramServiceClient())
{
svc.ClientCredentials.UserName.UserName = "*******";
svc.ClientCredentials.UserName.Password = "*******";
var request = new GetServiceDataProgramRequest()
{
CustomerAccountId = Convert.ToInt64(customerAccountId)
};
svc.Open();
var response = new GetServiceDataProgramResponse();
var metaData = new RequestMetadata()
{
ClientIPAddress = "xx.xx.xx.xx",
TrackingNumber = "1",
UserID = "1"
};
svc.GetAccountData(metaData, request, out response);
}
I am getting an error stating I am passing anonymous credentials with the request.
With the updated code, now I receive a different exception:
UPDATE:
After making suggested changes as well as removing the service call from the using block (per this article), I'm now getting a MessageSecurityException:
Error message:
-$exception {"The HTTP request is unauthorized with client authentication scheme 'Anonymous'.
The authentication header received from the server was 'Basic realm=\"Spring Security Application\"'."}
System.Exception {System.ServiceModel.Security.MessageSecurityException}
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory factory)
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factory, WebException responseException)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at [ServiceName].GetAccountData(svcRequest request)
at [ServiceName].GetAccountData(GetAccountDataRequest request)
in c:\\[Project]\\service references\\[ServiceName]\\reference.cs:line 3480
at c:\\[Project]\\service references\\[ServiceName](RequestMetadata RequestMetadata, ServiceRequest, ServiceResponse& ServiceResponse)
in c:\\[Project]\\service references\\[ServiceName]\\reference.cs:line 3487
at c:\\[Project]\\service references\\[ServiceName].CheckAccountForPaymentPlan(String customerAccountId)
in c:\\[Project]\\service references\\[ServiceName]\\\\PlanCheckService.cs:line 32
With TransportWithMessageCredential, you're specifying that you'll use message security to secure the individual messages, which requires the client credentials (username and password). You need something similar to the config from the msdn link as below:
<wsHttpBinding>
<binding name="WsHttpBinding_ICalculator">
<security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
This doesn't require specifying a certificate for client credentials. Transport security (using https and a valid ssl certificate) works in the same way a web site does, it doesn't require additional credentials/certs from the user. Valid certs from trusted certificate authorities are installed on the server (client machines are able to validate them) and the handshake process secures the channel. This does not require you to set clientCrdentials in config. You just need to install a valid cert (or test cert for dev) and configure the server config to point to it with something similar to:
<behaviors>
<serviceBehaviors>
<behavior name="mySvcBehavior">
<serviceCredentials>
<serviceCertificate findValue="contoso.com"
x509FindType="FindByIssuerName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
Try to remove the < transport clientCredentialType="Certificate" /> from your server config as a starter, update service refs and ensure your cert is working and configured correctly. Post your actual exceptions and more config if you still have problems.
For a good WCF source try: CodePlex, it helped me out no end when I started with WCF. The different application scenarios provide useful checklists to help ensure you don't miss any steps in your configuration.
Good Luck
UPDATE:
Once a channel is faulted, it needs to be recreated as you won't be able to communicate with the service until it's been reset. So add a check to recreate it:
If svc.State = CommunicationState.Faulted Then....
Try remove the svc.Open() line as I've never actually used that. I checked msdn for usage details but got about 2 lines of useless info. Once the service is setup you should be able to communicate with it without having to open it specifically. Not sure that this will actually make a difference though?!
Other things to check:
- can you right click on the service and view in browser without problems?
- in IIS can you view the certificate in directory security without any issues?
- debug up to the point before the service call is made and check the credentials are correctly assigned.
- check server event viewer for any info it may have logged with the request (if it's getting that far).
Also, here's some of the exception I trap to determine issues using ex.GetBaseException.GetType:
ServiceModel.EndpointNotFoundException
Server connection problem - either IIS isn't running or invalid server name
ServiceModel.Security.MessageSecurityException
Base Exception - ServiceModel.FaultException
"FailedAuthentication" - Bad credentials entered by user
"InvalidSecurity" - DB Error - either account has no access to DB, DB name in web config is incorrect, user password has expired in the database
"InvalidRequest" - Certificate accessibility issue - check service account has access to certificate
Base Exception - Net.WebException
Unauthorized access 401 - Check anonymous access in IIS is turned on
Base Exception - IdentityModel.Tokens.SecurityTokenValidationException
No service certificate is assigned in IIS
Base Exception - System.ServiceModel.CommunicationException
Identity mismatch with server certificate - eg in dev environment, cert named "localhost" so if you enter PC number or IP address for service you'll see this
Related
We noticed that our application which uses netTcpBinding suddenly stopped working. After examining the event log on the server which hosts the service, I found the following warning (one of series of many):
Exception information:
Exception type: TimeoutException
Exception message: The open operation did not complete within the allotted timeout of 00:01:00. The time allotted to this operation may
have been a portion of a longer timeout.
Server stack trace: at
System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan
timeout) at
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) at
System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) at
System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan
timeout, CallOnceManager cascade) at
System.ServiceModel.Channels.ServiceChannel.EnsureOpened(TimeSpan
timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String
action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins,
Object[] outs, TimeSpan timeout) at
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage
methodCall, ProxyOperationRuntime operation) at
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage
message)
To fix the issue, I had to increase the size of maxReceivedMessageSize by 10, so my new binding looks like this:
> <netTcpBinding>
> <binding name="largeBufferNetTcpBinding" listenBacklog="100" maxBufferSize="519730000" maxConnections="100"
> maxReceivedMessageSize="519730000" portSharingEnabled="true">
> <readerQuotas maxArrayLength="519730000"/>
> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
> <security>
> <message clientCredentialType="Windows"/>
> </security>
> </binding>
> <binding name="defaultNetTcpBinding" portSharingEnabled="true"/>
> <binding name="defaultNetTcpMexBinding" portSharingEnabled="true">
> <security mode="None"/>
> </binding> </netTcpBinding>
I don't understand how the maxReceivedMessageSize is related to the TimeoutException shown above. What can I do to troubleshoot this further and make the service more reliable?
Hard to tell exactly what's going on in your client/service interaction based on the info in your question but here are some things to try:
First, change everything that you set to 519730000 back to the default values except maxReceivedMessageSize which should be set to something in the range of 2 - 3 MB (start at 2097152 & increase until that message size exception goes away).
If that doesn't work, keep the same settings I've suggested but change the config to basicHttpBinding instead of netTcpBinding in both the service and client for this test. If this binding change works then it's very likely you're not properly disposing of the WCF client instances (ClientBase or channels from ChannelFactory). netTcpBinding depends on sessions and if the client instances aren't disposed of correctly, your code isn't releasing TCP resources efficiently on both the service & client. BTW: wrapping the client instance in using is not the right way either because of the funkiness of the WCF Dispose implementation.
If the calls still have timeouts, then you've pretty much eliminated bad TCP & client configuration and you should focus on the code performance of the service implementation.
I'm stuck with a problem of basic authentication to connect a web service which was created, in Visual Studio, deriving from ChannelBase. Already tried to ask in MSDN with no results, so I'm trying my luck over here.
This is the scenario:
a) the web service requires basic authentication (username and password) and supports SOAP 1.1
b) I have to go through a HTTP proxy which requires, again, basic authentication.
I cannot get this to work: the authentication on the web service is fine (if tested bypassing the proxy) but it looks like I cannot configure the ChannelBase-derived class to handle the authentication at the proxy (after getting an authentication required HTTP response it does not proceed negotiating the authentication).
here are my current settings in app.config
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="LocationWSBinding" receiveTimeout="00:02:30">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport authenticationScheme="Basic" proxyAddress="http://The.proxy.ip" useDefaultWebProxy="false"
bypassProxyOnLocal="True" proxyAuthenticationScheme="Basic" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://the.web.service"
binding ="customBinding" bindingConfiguration="LocationWSBinding"
contract="The.Contract.Name" name="LocationWSPort" />
</client>
</system.serviceModel>
</configuration>
and in the code, this is the place where I am specifying the credentials:
TheClient WS = new TheClient("LocationWSPort");
WS.ClientCredentials.UserName.UserName = "WebServiceUsername";
WS.ClientCredentials.UserName.Password = "WebServicepassword";
WS.ChannelFactory.Credentials.Windows.ClientCredential = new NetworkCredential("Proxyusername","ProxyPassword");
Can you please help me?
I don't know what's wrong which is blocking the client to proceed with proxy authentication when requested.
Thanks Emanuele
Enrico, sorry for the late answer.
I have tried following your suggestion, and it works only if I call a webservice method without arguments, when I call a method requiring complex types as an argument (which is the real call I have to make to get useful payload) I get the following error:
{"The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."}
[System.ServiceModel.CommunicationException]: {"The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."}
_className: null
_data: {System.Collections.ListDictionaryInternal}
_dynamicMethods: null
_exceptionMethod: null
_exceptionMethodString: null
_helpURL: null
_HResult: -2146233087
_innerException: null
_ipForWatsonBuckets: 83189596
_message: "The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."
_remoteStackIndex: 1
_remoteStackTraceString: "\r\nServer stack trace: \r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n"
_safeSerializationManager: {System.Runtime.Serialization.SafeSerializationManager}
_source: null
_stackTrace: {sbyte[80]}
_stackTraceString: null
_watsonBuckets: null
_xcode: -532462766
_xptrs: 0
Data: {System.Collections.ListDictionaryInternal}
HelpLink: null
HResult: -2146233087
InnerException: null
IsTransient: false
Message: "The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."
Source: "mscorlib"
StackTrace: "\r\nServer stack trace: \r\n at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)\r\n at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)\r\n\r\nException rethrown at [0]: \r\n at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)\r\n at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)\r\n at [.......] in E:\\VisualStudioPrj\\HTTPproxyTroubleshoot\\HTTPproxyTroubleshoot\\Program.cs:line 88"
TargetSite: {Void HandleReturnMessage(System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Messaging.IMessage)}
Now, I am sure that the data contract is OK (it is working if I do not set WebRequest.DefaultWebProxy).
As for the FTP, well, these SOAP calls are done by one of the two threads run by the Windows service which coordinates them: the other is taking care of FTP calls instead, and I need these not to transit through the proxy otherwise the answer will be formatted as HTML which is messing around the FTP communications...
The problem you're experiencing is due to the fact that WCF will use the same set of client credentials both for service authentication and for upstream web proxy authentication. Since your service requires a different pair of credentials than your upstream web proxy does, the request is not being properly authenticated and subsequently gets blocked.
Since you can specify only a single set of credentials on the ClientCredentials class, the solution is to set the credentials to use with the web proxy at a lower level, using the WebRequest.DefaultWebProxy property.
Here's an example:
var webProxy = new WebProxy
{
Address = "http://myproxy:8080",
Credentials = new NetworkCredential("username", "password")
};
WebRequest.DefaultWebProxy = webProxy;
You should then tell WCF to use the default proxy that was just configured through the WebProxy class:
<bindings>
<customBinding>
<binding name="MyBinding">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport authenticationScheme="Basic"
useDefaultWebProxy="true"
bypassProxyOnLocal="true"
proxyAuthenticationScheme="Basic" />
</binding>
</customBinding>
</bindings>
Related resources:
How to supply dedicated credentials for web proxy authentication in a WCF client
I am trying to host the same service inside the same website using two endpoints one HTTP and the other HTTPS.
The problem is I can happily call one service (whichever I call first works) but then call to the other fails until I iisreset and try again. SO I can happily call both but one at a time until a process recycle.
I have exhausted (or I think I have) all relevant Q&A and documents that I could find and none of them seem to help me. The level of documentation of MSDN regarding these cases (which should be faily common) is appalling and nothing works as it seems.
I have used Host/BaseAddresses/BaseAddress with no luck and whenever I used it, I get another error (cannot find an HTTPS scheme).
I have folder called Secure which is enabled for HTTPS inside IIS.
I have disabled mex so no metadata allowed which I got passed previous errors.
Anyone got a clue? I am cluless...
<service name="Namespace.MyService" behaviorConfiguration="MyBehaviour">
<host>
<baseAddresses>
</baseAddresses>
</host>
<endpoint address="http://localhost/Services/MyService.svc/MyService"
name="MyService" binding="wsHttpBinding"
bindingConfiguration="myWsHttpBinding" contract="Namespace.IMyService" />
<endpoint address="https://localhost/Services/Secure/MySslService.svc/MySslService"
name="MySslService" binding="basicHttpBinding"
bindingConfiguration="MySslServiceBinding" contract="Namespace.IMyService" />
</service>
.....
<behavior name="MyBehaviour">
<custom1/>
<custom2/>
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="CustomProvider"/>
<serviceCredentials>
<serviceCertificate findValue="Some" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="SomeClass, SomeDll"/>
</serviceCredentials>
</behavior>
Original error is:
A registration already exists for URI
'http://localhost/Services/....'.
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: A
registration already exists for URI
'http://localhost/Services/....'.
Source Error:
An unhandled exception was generated
during the execution of the current
web request. Information regarding the
origin and location of the exception
can be identified using the exception
stack trace below.
Stack Trace:
[InvalidOperationException: A
registration already exists for URI
System.ServiceModel.Channels.UriPrefixTable1.RegisterUri(Uri
uri, HostNameComparisonMode
hostNameComparisonMode, TItem item)
+320 System.ServiceModel.Channels.HttpTransportManager.Register(TransportChannelListener
channelListener) +380
System.ServiceModel.Channels.TransportManager.Open(TransportChannelListener
channelListener) +816
System.ServiceModel.Channels.TransportManagerContainer.Open(SelectTransportManagersCallback
selectTransportManagerCallback) +121
System.ServiceModel.Channels.HttpChannelListener.OnOpen(TimeSpan
timeout) +125
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) +789
System.ServiceModel.Channels.DatagramChannelDemuxer2.OnOuterListenerOpen(ChannelDemuxerFilter
filter, IChannelListener listener,
TimeSpan timeout) +606
System.ServiceModel.Channels.SingletonChannelListener`3.OnOpen(TimeSpan
timeout) +91
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout) +789
System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan
timeout) +375
Here is the WCF Trace log error:
The ChannelDispatcher at
'http://localhost/Services/...' with
contract(s) '"IMyService"' is unable
to open its IChannelListener.
System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan
timeout)
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout)
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan
timeout)
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan
timeout)
System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(String
normalizedVirtualPath)
System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String
normalizedVirtualPath)
System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String
relativeVirtualPath)
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object
state)
System.ServiceModel.AspNetPartialTrustHelpers.PartialTrustInvoke(ContextCallback
callback, Object state)
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequestWithFlow(Object
state)
System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32
errorCode, UInt32 numBytes,
NativeOverlapped* nativeOverlapped)
System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32
error, UInt32 bytesRead,
NativeOverlapped* nativeOverlapped)
System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32
errorCode, UInt32 numBytes,
NativeOverlapped* pOVERLAP)
OK, I fixed my problem. The issue was I was under impression that I had to create a secure folder in IIS and put the .svc file in there to guarantee SSL transmission. It turned out that it is not the case and all I had to do was to use the same .svc file and just define 2 endpoints:
<endpoint address="http://localhost/Services/MyService.svc/MyService"
name="MyService" binding="wsHttpBinding"
bindingConfiguration="myWsHttpBinding" contract="Namespace.IMyService" />
<endpoint address="https://localhost/Services/MyService.svc/MySecureService"
name="MySslService" binding="basicHttpBinding"
bindingConfiguration="MySslServiceBinding" contract="Namespace.IMyService" />
It's possible the nested directories are confusing IIS - I have had this happen before although not in the WCF context. Try changing the endpoint addresses to:
address="http://localhost/MyService"
address="https://localhost/MySslService"
Can you try hosting the application from a console app instead of IIS? I'm pretty certain IIS is causing your problem somehow but this might confirm it.
If it's possible, I'd also try to isolate the problem by removing some of the configuration detail - eg bindingConfiguration and behaviorConfiguration.
Obviously these are just debugging steps I'd perform (you may have already) to try and isolate the problem - sorry I haven't got a full answer.
I am trying to host my service using following configuration.
<system.serviceModel>
<services>
<service name="Test.MyService" behaviorConfiguration="MyServiceBehavior">
<!-- Service Endpoints -->
<endpoint address="MyTestService" binding="wsHttpBinding" bindingConfiguration="WebserviceHttpBinding" contract="Test.IMyService"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="WebserviceHttpBinding">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="false"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Test.CredentialValidator, Test"/>
<serviceCertificate findValue="RPKey" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My"/>
</serviceCredentials>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
When I debug this service, I am having no problem. I hosted this service in IIS using Website. When I browse this service from IIS I am getting following exception.
Server Error in '/MyTestService'
Application.
Keyset does not exist
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.Security.Cryptography.CryptographicException:
Keyset does not exist
Source Error:
An unhandled exception was generated
during the execution of the current
web request. Information regarding the
origin and location of the exception
can be identified using the exception
stack trace below.
Stack Trace:
[CryptographicException: Keyset does not exist
]
System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer) +369
System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle) +151
System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair() +85
System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize) +280
System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey() +468
System.ServiceModel.Security.SecurityUtils.EnsureCertificateCanDoKeyExchange(X509Certificate2 certificate) +85
[ArgumentException: The certificate 'CN=RPKey' must have a private key that is capable of key exchange. The process must have access rights for the private key.]
System.ServiceModel.Security.SecurityUtils.EnsureCertificateCanDoKeyExchange(X509Certificate2 certificate) +15832031
System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider() +45
System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement) +73
System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement) +65
System.ServiceModel.Security.SessionRenewSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement) +14
System.ServiceModel.Security.SymmetricSecurityProtocolFactory.OnOpen(TimeSpan timeout) +15334232
System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) +23
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.Security.SecurityListenerSettingsLifetimeManager.Open(TimeSpan timeout) +101
System.ServiceModel.Channels.SecurityChannelListener1.OnOpen(TimeSpan timeout) +203
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout) +87
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +110
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.OnOpen(TimeSpan timeout) +149
System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) +23
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.Security.SecurityUtils.OpenCommunicationObject(ICommunicationObject obj, TimeSpan timeout) +24
System.ServiceModel.Security.SecuritySessionServerSettings.OnOpen(TimeSpan timeout) +878
System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) +23
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.Security.SecurityListenerSettingsLifetimeManager.Open(TimeSpan timeout) +153
System.ServiceModel.Channels.SecurityChannelListener1.OnOpen(TimeSpan timeout) +203
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout) +87
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +110
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +135
System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +654
[ServiceActivationException: The service '/AtlasServices/Service.svc' cannot be activated due to an exception during compilation. The exception message is: The certificate 'CN=RPKey' must have a private key that is capable of key exchange. The process must have access rights for the private key..]
System.ServiceModel.AsyncResult.End(IAsyncResult result) +15700960
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +15623609
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(HttpApplication context, Boolean flowContext) +265
System.ServiceModel.Activation.HttpModule.ProcessRequest(Object sender, EventArgs e) +227
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
This is propabely because on the IIS the account settings are different from the account that you are using to run a local server.
are you working with an X.509 certificate?
If so, are you sure you gave read acces privileges to the account that is running the process (that is running IIS) for the file containing the private key?
So if IIS is running under the account Saghar, does Saghar have read privilesges for the key file ?
#update
ArgumentException: The certificate 'CN=RPKey' must have a private key that is capable of key exchange. The process must have access rights for the private key.
this tells me that your IIS account doesn't have permissions to your private key
Based on the exception report, it sounds like one of two things may be happening. The certificate you are referencing was either installed into the keystore with only the public key, or the installed certificate is restricted and the account your application pool is running under does not have permission to access the key.
The former case is very easy to run into if the x.509 certificate was not exported as a .pfx, but instead as a .cer. To exchange certificates that contain a private key, the .cer format is insufficient, as it can only contain a DER encoded x.509 public key certificate. You must export your certificate from a certificate server (or from a store that has both keys) as a .pfx file, and make sure you include the private key.
If it is the latter case, then you need to make sure the account of the application pool your service is running under in IIS has permission to access the certificate store the keys are contained within. This article may be helpful: Make X.509 Certificates Accessible to WCF
IIS application pool identity does not have access to certificate
Set application pool identity to NETWORK SERVICE
install rktools
run "c:\Program Files (x86)\Windows Resource Kits\Tools\winhttpcertcfg.exe" -g -c LOCAL_MACHINE\My -s WSE2QuickStartServer -a "NETWORK SERVICE"
If you do not require wshttpbinding, just try changing to basichttpbinding and your issue will probably go away.
We had trouble with this in the past when the client was connecting from a site outside our network. Since the connection was over vpn we had the option of abandoning security for the binding.
I have a WCF service with the following configuration:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MetadataEnabled">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MetadataEnabled" name="MyNamespace.MyService">
<endpoint name="BasicHttp"
address=""
binding="basicHttpBinding"
contract="MyNamespace.IMyServiceContract" />
<endpoint name="MetadataHttp"
address="contract"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/myservice" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
When hosting the service in the WcfSvcHost.exe process, if I browse to the URL:
http://localhost/myservice/contract
where the service metadata is available I get an HTTP 400 Bad Request error.
By inspecting the WCF logs I found out that an System.Xml.XmlException exception is being thrown with the message: "The body of the message cannot be read because it is empty."Here is an extract of the log file:
<Exception>
<ExceptionType>
System.ServiceModel.ProtocolException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
</ExceptionType>
<Message>There is a problem with the XML that was received from the network. See inner exception for more details.</Message>
<StackTrace>
at System.ServiceModel.Channels.HttpRequestContext.CreateMessage()
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<InnerException>
<ExceptionType>System.Xml.XmlException, System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The body of the message cannot be read because it is empty.</Message>
<StackTrace>
at System.ServiceModel.Channels.HttpRequestContext.CreateMessage()
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)
at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContext(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
</InnerException>
</Exception>
If I instead browse to the URL:
http://localhost/myservice?wsdl
everything works just fine and I get the WSDL contract. At this point, I can also remove the "MetadataHttp" metadata endpoint completely, and it wouldn't make any difference.
I'm using .NET 3.5 SP1. Does anyone have an idea of what could be wrong here?
I think I found out what the problem is.
If I browse to the URL:
http://localhost/myservice/contract
with the WcfTestClient application I can successfully retrieve the service metadata.
So the error really only occurs when I request the URL through a web browser.
The HTTP Bad Request error comes from the fact that the browser issues an HTTP GET request where the contents of the message are in the HTTP headers, and the body is empty.This is exactly what the WCF mexHttpBinding is complaining about!
In order to get to the service contract via a web browser you will have to explicitly enable it in the service behavior:
<serviceBehaviors>
<behavior name="MetadataEnabled">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
The URL to request becomes then:
http://localhost/myservice?wsdl
So, it turns out I was a little too quick in posting this question. However, I'll keep it anyway just for the record.
Generally this is a problem with the size of the SOAP envelop. Check your binding configuration in order to change MaxBufferPoolSize, MaxReceivedMessageSize to allow huge contents. Remember, you must change both in client and server sides.
Another issue is the MessageEnconding (another binding parameter), ensure that client and server side are using the same encoding.
Finally, check the Reader Quotas Properties parameters.
I was able to fix the "400 Bad Request" problem by switching my WCF service from running off of Visual Studio Development Server to using Local IIS Web server (right click on the project --> properties --> web tab --> radio button under "Servers"). I hope this helps someone out there because it took me two days to figure this one out.
I had HTTP 400 issues when consuming a HTTPS mex URL from SvcUtil, eventhough httpsGetEnabled was set to true. The error message was miles away from what was really the issue so I'm posting here in case anyone else stumbles upon the same problem.
I had a self-signed CA certificate (TestRootCA) which was the issuer of the server certificate (localhost). On the client I imported the TestRootCA CER file but I didn't import the CRL (Certificate Revocation List). It seems that when you use a self-signed CA you must also import the CRL, otherwise server authentication fails in strange ways, none of which point you to the real problem. What's worse is that the fail happens during SSL handshake, before the request even reaches your service, so you'll see no errors in WCF trace logs.
If you are not sure why your WCF code is throwing an error then I highly recommend the MS Service Trace Viewer. It is very helpful in identifying communication and transport issues like this. Take a look at this C# Corner article for more details.