0

I get the error "{"The remote server returned an error: (403) Forbidden."} The HTTP request was forbidden with client authentication scheme 'Anonymous'." when using basicHttpBinding with Transport security and certificate credential. My service is in amazon ec2 instance and my client app remotely connect to it over the internet. I am able to connect to the wcf service if my Transport credential is set to "None" in both the web.config of the service and app.config of the client. My service certificate is like "www.example.com" is installed on amazon ec2 "local machine store" and "Personal Folder". My client app certificate is just a self-signed certificate which I installed to its "local machine and Personal Folder" and also to the "Trusted People store" in the amazon ec2 instance where my wcf service is. I have also setup "https" to my IIS site bindings and I can reach the site through like "https://www.example.com"

Below is the web.config, app.config, and the code I have on the client app.

Service Web.config:

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <customErrors mode="Off"/>
  </system.web>
  <system.serviceModel>

   <bindings>
      <basicHttpBinding>
        <binding name="basicHttpBinding_Config" >
          <security mode="Transport">
            <transport clientCredentialType="Certificate"/>
          </security>
        </binding>        
      </basicHttpBinding>
  </bindings>

    <services>    
      <service name="MyProject.MyService">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpBinding_Config"
          contract="MyService"  />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>          
          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="PeerOrChainTrust" trustedStoreLocation="LocalMachine"/>
            </clientCertificate>
            <serviceCertificate findValue="www.example.com" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
          </serviceCredentials>          
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Client app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>                   

      <bindings>
        <basicHttpBinding>
          <binding name="basicHttpBinding_Config" >
            <security mode="Transport">
              <transport clientCredentialType="Certificate"/>
            </security>
          </binding>
        </basicHttpBinding>
      </bindings>
      <client>
        <endpoint address="https://www.example.com/sub/Service1.svc"
          binding="basicHttpBinding" bindingConfiguration="basicHttpBinding_Config"
          contract="ServiceReference1.MyService" name="BasicHttpBinding_MyService" />
      </client>

      <behaviors>
        <endpointBehaviors>
          <behavior>
            <clientCredentials>
              <clientCertificate findValue="clientKey"
                                 storeLocation="LocalMachine"
                                 storeName="My"
                                 x509FindType="FindBySubjectName" />
            </clientCredentials>

          </behavior>
        </endpointBehaviors>
      </behaviors>

    </system.serviceModel>
</configuration>

Client App Console Code:

static void Main(string[] args)
        {
            System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate { return true; }; 

            ServiceReference1.MyServiceClient client = new ServiceReference1.MyServiceClient();
            string[] a = client.GetMethods(ref mh);
            foreach (string s in a)
            {
                Console.WriteLine(s);
            }
            Console.ReadKey();

        }

The Diagnostic Tracing returns below:

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>131077</EventID>
<Type>3</Type>
<SubType Name="Critical">0</SubType>
<Level>1</Level>
<TimeCreated SystemTime="2018-11-16T21:50:58.8220239Z" />
<Source Name="System.ServiceModel" />
<Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
<Execution ProcessName="ConsoleApplication1" ProcessID="22220" ThreadID="1" />
<Channel />
<Computer>DESKTOP-RPNI11M</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Critical">
<TraceIdentifier>UnhandledException</TraceIdentifier>
<Description>Unhandled exception</Description>
<AppDomain>ConsoleApplication1.exe</AppDomain>
<Exception>
<ExceptionType>System.ServiceModel.Security.MessageSecurityException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The HTTP request was forbidden with client authentication scheme 'Anonymous'.</Message>
<StackTrace>
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory`1 factory)
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory`1 factory, WebException responseException, ChannelBinding channelBinding)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, 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)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&amp; msgData, Int32 type)
at ConsoleApplication1.ServiceReference1.IOD_V416.GetMethods(GetMethodsRequest request)
at ConsoleApplication1.ServiceReference1.OD_V416Client.ConsoleApplication1.ServiceReference1.IOD_V416.GetMethods(GetMethodsRequest request) in D:\Workfolder\Projects2\MyProject\WCF_App\MyService - Copy (3)\ConsoleApplication1\Service References\ServiceReference1\Reference.cs:line 72017
at ConsoleApplication1.ServiceReference1.OD_V416Client.GetMethods(MultiSpeakMsgHeader&amp; MultiSpeakMsgHeader) in D:\Workfolder\Projects2\MyProject\WCF_App\MyService - Copy (3)\ConsoleApplication1\Service References\ServiceReference1\Reference.cs:line 72023
at ConsoleApplication1.Program.Main(String[] args) in D:\Workfolder\Projects2\MyProject\WCF_App\MyService - Copy (3)\ConsoleApplication1\Program.cs:line 43
</StackTrace>
<ExceptionString>System.ServiceModel.Security.MessageSecurityException: The HTTP request was forbidden with client authentication scheme 'Anonymous'. ---&gt; System.Net.WebException: The remote server returned an error: (403) Forbidden.
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   --- End of inner exception stack trace ---

Server stack trace: 
   at System.ServiceModel.Channels.HttpChannelUtilities.ValidateAuthentication(HttpWebRequest request, HttpWebResponse response, WebException responseException, HttpChannelFactory`1 factory)
   at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory`1 factory, WebException responseException, ChannelBinding channelBinding)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, 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)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&amp; msgData, Int32 type)
   at ConsoleApplication1.ServiceReference1.IOD_V416.GetMethods(GetMethodsRequest request)
   at ConsoleApplication1.ServiceReference1.OD_V416Client.ConsoleApplication1.ServiceReference1.IOD_V416.GetMethods(GetMethodsRequest request) in D:\Workfolder\Projects2\MyProject\WCF_App\MyService - Copy (3)\ConsoleApplication1\Service References\ServiceReference1\Reference.cs:line 72017
   at ConsoleApplication1.ServiceReference1.OD_V416Client.GetMethods(MultiSpeakMsgHeader&amp; MultiSpeakMsgHeader) in D:\Workfolder\Projects2\MyProject\WCF_App\MyService - Copy (3)\ConsoleApplication1\Service References\ServiceReference1\Reference.cs:line 72023
   at ConsoleApplication1.Program.Main(String[] args) in D:\Workfolder\Projects2\MyProject\WCF_App\MyService - Copy (3)\ConsoleApplication1\Program.cs:line 43</ExceptionString>
<InnerException>
<ExceptionType>System.Net.WebException, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The remote server returned an error: (403) Forbidden.</Message>
<StackTrace>
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
</StackTrace>
<ExceptionString>System.Net.WebException: The remote server returned an error: (403) Forbidden.
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)</ExceptionString>
</InnerException>
</Exception>
</TraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>

I did look to other similar issues but none has solve it yet, I'm continuously searching for the right solutions, I appreciate any help or advice the community provides.

2 Answers2

0

EDIT:

Probably better to use a sha2 cert:

makecert -len 2048 -r -a sha256 -sv private.pvk -n CN=yoursubjectname cert.cer
pvk2pfx -spc cert.cer -pvk private.pvk -pfx out.pfx

Install the private cert(.pfx) on the host, set in IIS app, and install the public cert on the client(.cer), you will have to install in both personal and trusted people stores.

EDIT: I think you also need to give your behavior a name in the host and client config and assign that behavior to your endpoints.

Host:

<behavior name="serviceBahavior"> 
<service name="MyProject.MyService" behaviorConfiguration="serviceBahavior">

Client:

<behavior name="clientBahavior">
<endpoint address="https://www.example.com/sub/Service1.svc"
          binding="basicHttpBinding" bindingConfiguration="basicHttpBinding_Config"
          contract="ServiceReference1.MyService" name="BasicHttpBinding_MyService" behaviorConfiguration="clientBahavior" />

I think since you are using <security mode="Transport"> you will need to make your mex as HTTPS:

 <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />

Also set in your behavior the serviceMetadata

from: <serviceMetadata httpGetEnabled="true"/>

to <serviceMetadata httpsGetEnabled="true"/>

Also, make sure your transport in the client config matches the host config:

<transport clientCredentialType="Certificate"/>

Popo
  • 2,402
  • 5
  • 33
  • 55
  • Thank you for your response Popo, I changed my mex to https and serviceMetadata to httpsGetEnabled, also the clientCredentialsType to Certificate in the client config, however, the error remains. – user2717643 Nov 14 '18 at 02:20
  • @user2717643 I updated my answer, see if that solves your problem. – Popo Nov 14 '18 at 19:16
  • Thanks again Popo, I followed your suggestion and my service config now looks below: – user2717643 Nov 15 '18 at 18:41
  • Yes Popo, I'm still getting the same error, I'm sorry i thought I mentioned it in my response above. – user2717643 Nov 15 '18 at 19:57
  • @user2717643 have you tried using system.diagnostics to log trace files to see if you get a better informational error message? https://stackoverflow.com/a/4271597/2016162 I built a sample project and configured it exactly like yours using a self signed cert and it works great. There must be some other underlying issue. – Popo Nov 15 '18 at 20:49
  • Thank you again Popo, I enabled the tracing on the client app and added to my question above, I noticed unhandled exception but i think it refers to the main issue which is The HTTP request was forbidden with client authentication scheme 'Anonymous' – user2717643 Nov 16 '18 at 22:27
  • By the way, I forgot to indicate that I am using VS2010, I'm not sure if that somehow cause the error because I tried to run my client app on VS2017 which seem to work at first, but after a few hours the same error appears. I'm suspecting it must be the self signed certificate that my client app use that may be causing the error. i will try to generate a new self sign certificate. – user2717643 Nov 16 '18 at 22:31
  • @user2717643 if you have multiple certs with the same subject name that could cause problem, you can alway find the cert by thumbprint instead of subject name. – Popo Nov 17 '18 at 03:49
  • This is how i generate my client certificate "makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=WcfClient -sky exchange -pe" do you think this is correct? Can you please advise me on how to make correct self-signed certificate in the client app that will eb use to validate the client to the server. Thanks again! – user2717643 Nov 17 '18 at 07:48
  • @user2717643 I updated the answer to include makecert info, you have to use the same cert on both host and client, host has private key, and client has public key. – Popo Nov 17 '18 at 17:31
  • Hi Popo, thanks again, I followed your makecert instruction and was able to generate and import the cert.cer file. However I get an error with the file out.pfx whenever i enter the Password to import it. I repeat the procedure twice but I get the same error that reads like "The password you entered is incorrect". – user2717643 Nov 20 '18 at 06:35
  • Were you prompted to enter a password when you created the .pfx? For testing I usually don’t enter a password, I just leave it blank. – Popo Nov 20 '18 at 11:58
  • Hi Popo, I am now able to create and import the certificate with your recommended makecert statement without password, however same error shows up. I have first imported the out.pfx on my client app computer and the cert.cer on my service computer where my host certificate www.example.com is located, same The HTTP request was forbidden with client authentication scheme 'Anonymous'. – user2717643 Nov 20 '18 at 14:20
  • Next I tried to import the cert.cer on my client app computer and the out.pfx on my service computer but the same issue shows up. All those time I have imported the keys on Personal and TrustedPeople store on localmachine – user2717643 Nov 20 '18 at 14:21
  • @user2717643 did you assign in IIS the out.pfx as the https cert? – Popo Nov 20 '18 at 16:03
  • In my IIS, the https certificate I use for my service is www.example.com which we get from our certificate authority. But I installed the certs on Personal and TrustedPeople store on both client app and service machine. – user2717643 Nov 20 '18 at 16:38
  • @user2717643 whichever cert you are using in IIS you need the public version of that on the client. – Popo Nov 20 '18 at 17:11
  • Yes Popo, i have the public key of our www.example.com in our client computer – user2717643 Nov 20 '18 at 18:17
  • @user2717643 then you don't need the self signed cert, just make sure your client web.config is referencing the your www.example.com cert in the behavior – Popo Nov 20 '18 at 18:22
  • Hello Popo, sorry for my delayed response. I reference the www.example.com cert in my client computer by installing it in the trustedpeople store, this I think will identify and authenticate my service computer to the client app. But, also I'm thinking that my Service likewise needs to identify and authenticate my client app this is why I'm using signed certificate for it. – user2717643 Nov 22 '18 at 15:30
  • After countless trials, I stumbled upon a solution that works for me, this is by installing the root key use for my self-signed certificate in the service computer under the store "Trusted Root Certification Authorities". Then I use that root key to create a self signed cert which I use in my client app.config to identify itself to my service app. – user2717643 Nov 22 '18 at 15:35
  • Thank you so much Popo for all your help and time you spent to answer this error. It's really good to have some help to validate every possible option. – user2717643 Nov 22 '18 at 15:52
  • @user2717643 glad to hear you got it working, and glad to try to help. I have spent a lot of time developing wcf services and sometimes it can be real tricky when dealing with different authentications. – Popo Nov 22 '18 at 20:02
0

I found the following solution that works for this error I encountered.

First, I created a certificate as follows:

makecert -n "CN=MyRootSigningKey" -r -sv MyRootSigningKey.pvk MyRootSigningKey.cer

Second, I treat this as my root key and install it in my AWS service under the certificate store "Trusted Root Certification Authorities" using mmc.

Third, I created a self-signed cert using the root key "MyRootSigningKey" as follows:

makecert -sk MySignedKeyName -iv MyRootSigningKey.pvk -n "CN=MySignedKey" -ic MyRootSigningKey.cer -sr localmachine -ss my -sky exchange -pe

Last, I reference the self signed cert "MySignedKey" in my client app config like below:

<client>        
        <endpoint address="https://www.example.com/sub/Service1.svc" behaviorConfiguration="clientBehavior"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_MyService"
          contract="ServiceReference1.MyService"  />

      </client>

      <behaviors>
        <endpointBehaviors>
          <behavior name="clientBehavior">
            <clientCredentials>
              <clientCertificate findValue="MySignedKey"
                                 storeLocation="LocalMachine"
                                 storeName="My"
                                 x509FindType="FindBySubjectName" />
            </clientCredentials>

          </behavior>
        </endpointBehaviors>
      </behaviors>