Self-Hosted WCF Service with Mutual SSL (between Service and Client) fails with 403 Forbidden

£可爱£侵袭症+ 提交于 2019-11-30 15:54:15

here is the demo for your reference. i tested it under win7 + vs2010 + client-server-on-same-machine.

server side:

[ServiceContract(Name="CalculatorService")]
    public interface ICalculatorService {
        [OperationContract]
        int Add(int x, int y);
    }

public class CalculatorService : ICalculatorService {
        public Int32 Add(Int32 x, Int32 y) {
            Console.WriteLine("{0}: service method called (x = {1}, y = {2})",
                Thread.CurrentThread.ManagedThreadId, x, y);
            return x + y;
        }
    }

class Program {
        static void Main(string[] args) {
            ServicePointManager.ServerCertificateValidationCallback +=
                (sender, certificate, chain, sslPolicyErrors) => true;

            using (var serviceHost = new ServiceHost(typeof(CalculatorService))) {
                serviceHost.Opened += delegate {
                    Console.WriteLine("{0}: service started", 
                        Thread.CurrentThread.ManagedThreadId);
                };
                serviceHost.Open();
                Console.Read();
            }
        }
    }

<?xml version="1.0" encoding="utf-8" ?> <configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="transportSecurity">
                    <security mode="Transport">
                        <transport clientCredentialType="Certificate"/>
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>

        <services>
            <service name="WcfService.CalculatorService">
                <endpoint address="https://hp-laptop:3721/calculatorservice"
                          binding="wsHttpBinding"
                          bindingConfiguration="transportSecurity"
                          contract="Contract.ICalculatorService" />
            </service>
        </services>
    </system.serviceModel> </configuration>

Client side:

class Program {
        static void Main(string[] args) {
            using (var channelFactory =
                new ChannelFactory<ICalculatorService>("calculatorservice")) {
                ICalculatorService proxy = channelFactory.CreateChannel();
                Console.WriteLine(proxy.Add(1, 2));
                Console.Read();
            }
            Console.Read();
        }
    }

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="transportSecurity">
                    <security mode="Transport">
                        <transport clientCredentialType="Certificate"/>
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <behaviors>
            <endpointBehaviors>
                <behavior name="defaultClientCertificate">
                    <clientCredentials>
                        <clientCertificate 
                            storeLocation="LocalMachine" 
                            storeName="My" 
                            x509FindType="FindBySubjectName" 
                            findValue="client1"/>
                    </clientCredentials>
                </behavior>
            </endpointBehaviors>
        </behaviors>
        <client>
            <endpoint name="calculatorservice" behaviorConfiguration="defaultClientCertificate"
                      address="https://hp-laptop:3721/calculatorservice"
                      binding="wsHttpBinding"
                      bindingConfiguration="transportSecurity"
                      contract="Contract.ICalculatorService"/>
        </client>
    </system.serviceModel>
</configuration>

Certificate creation:

self-created CA

makecert -n "CN=RootCA" -r -sv c:\rootca.pvk c:\rootca.cer

After creation, import this certificate into 'Trusted Root Certification' by Certificates Console. This is the step to stop the exception you mentioned.

certificate for service program

makecert -n "CN=hp-laptop" -ic c:\rootca.cer -iv c:\rootca.pvk -sr LocalMachine -ss My -pe -sky exchange

Note that the CN value above should match to the DNS part of service address. e.g. hp-laptop is my computer name. the address of service endpoint will be "https://google.com:/...". (replace google dot com with 'hp-laptop' due to some stackoverflow rules).

register service certificate with service program:

netsh http add sslcert ipport=0.0.0.0:3721 certhash=‎6c78ad6480d62f5f460f17f70ef9660076872326 appid={a0327398-4069-4d2d-83c0-a0d5e6cc71b5}

The certhash value is the thumbprint of service program certificate (check with Certificates Console). the appid is the GUID from service program file "AssemblyINfo.cs".

Certicate for client program:

makecert -n "CN=client1" -ic c:\rootca.cer -iv c:\rootca.pvk -sr LocalMachine -ss My -pe -sky exchange

UPDATE: according to typhoid's experience with this solution, the 'Anonymous' exception is still there due to too many trusted root authorities in that server. Two links was provided by typhoid to solve this issue.

http://support.microsoft.com/kb/2464556

http://blog.codit.eu/post/2013/04/03/Troubleshooting-SSL-client-certificate-issue-on-IIS.aspx

Another option would be to monitor the actual exchange including the ssl v3 handshake. I ran into something similar and eventually discovered that the SSL Handshake was failing because I had too many trusted certificate authorities. To resolve this, I needed to follow this resolution. The resolution keeps the SSL portion of windows from sending the trusted certificate authorities back to the client; thus, allowing the handshake to complete.

Mike

I had the same problem for several days until finally, I realized that this line creates a "Server Authentication" certificate. You need a "Client Authentication" certificate or the client certificate will be considered invalid.

makecert -pe -n "CN=client1" -ss my -sr LocalMachine -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -in "SelfSignedCA" -is Root -ir LocalMachine -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 Client1.cer

So just use a 2 for the -eku parameter instead: 1.3.6.1.5.5.7.3.2

The SSL handshake must complete successfully before any HTTP traffic can go through. The fact that you are getting an HTTP response at all indicates that your SSL setup is working fine.

The 403 Forbidden response indicates that the server is set up to require HTTP basic auth username and password before providing the resource/page. You need your clients to provide the basic auth username/password at the HTTP level, in addition to what you are already doing at the TLS/SSL level, or you need to set up the server to allow access without HTTP basic auth.

I faced the same issue (Forbidden 403) for my client trying to connect to server using MutualAuthentication. It is true that SSL layer is not an issue and at the same time the Server did not get the request as it was blocked by transport layer. But I was facing this for 2012 server only where-as 2008 would work just fine. Enabling the serviceSecurityAudit at the Service configuration via

<serviceSecurityAudit auditLogLocation="Application"
            suppressAuditFailure="false" 
            serviceAuthorizationAuditLevel="SuccessOrFailure" 
            messageAuthenticationAuditLevel="SuccessOrFailure" />

showed the error/failure in the Windows Event log application side as Message Authentication and Service Authorization errors.

The solution which worked for me was to introduce a custom certificate validator for clientCertificate via

   <clientCertificate>              <authentication certificateValidationMode="Custom" customCertificateValidatorType="App.Web.Framework.MyX509CertificateValidator, App.Vertical.Send"  />
   </clientCertificate>

This would require one to implement Certificate validation method in the assembly.

I can provide that detail as well if needed.

Thanks --Kirti Kunal Shah

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!