简体   繁体   English

具有相互SSL的自托管WCF服务(在服务和客户端之间)失败,403 Forbidden

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

I'm trying to setup a demo of Mutual SSL between a self-hosted WCF service and a client app (command prompt for now). 我正在尝试在自托管 WCF服务和客户端应用程序之间设置Mutual SSL演示(现在是命令提示符)。 In the end I'm trying to get to a solution where I have transport security (not message security) between a server that uses a certificate for its incoming connections and multiple clients each with individual certs that I can use to uniquely identify each of the clients. 最后,我试图找到一个解决方案,我在使用证书进行传入连接的服务器和多个客户端之间有传输安全性 (而不是消息安全性),每个客户端都有单独的证书,我可以用它来唯一地标识每个客户端。

I've tried a number of differing approaches to this, but none have worked (I was unable to find an exact example for what I've been trying to do). 我已经尝试了许多不同的方法,但没有一个有效(我无法找到一个确切的例子,我一直在尝试做)。 Each time I think I'm getting close I end up with an exception in the client when I try to invoke the service. 每次我认为我越来越近,当我尝试调用服务时,我最终会在客户端出现异常。 The most common exception I've run into is: 我遇到的最常见的例外是:

“The HTTP request was forbidden with client authentication scheme 'Anonymous'.”
Inner exception: "The remote server returned an error: (403) Forbidden."

Does anyone have any thoughts on what I might have done wrong or perhaps a better walk through of how to setup mutual SSL in the above scenario? 有没有人对我可能做错了什么或者更好地了解如何在上述场景中设置相互SSL?

Full disclosure - as of right now I am running both the client and server on the same computer. 完全披露 - 截至目前,我在同一台计算机上运行客户端和服务器。 Not sure if that matters. 不确定是否重要。

Config snippets below 下面的配置片段

The service and client code is relatively trivial so I'm pretty confident that I've gotten them to work. 服务和客户端代码相对微不足道,所以我非常有信心我已经让他们工作了。 The app configs (specifically the bindings and behaviors) and certs are “more interesting” so I'm not as confident there. 应用程序配置(特别是绑定和行为)和证书“更有趣”,所以我不那么自信。

How I created the certs (actual commands verbatim) 我是如何创建证书的(逐字实际命令)

makecert -pe -n "CN=SelfSignedCA" -ss Root -sr LocalMachine  -a sha1 -sky signature -r -sv "SelfSignedCA.cer" "SelfSignedCA.pvk"
makecert -pe -n "CN=system" -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 Service.cer
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

Associating the Cert with the port (actual commands verbatim) 将证书与端口关联(实际命令逐字)

netsh http add urlacl url=https://+:44355/MyService/ user=EVERYONE

Server Settings 服务器设置

Bindings: 绑定:

  <wsHttpBinding>
    <binding name="CustomBinding">      
      <security mode="Transport">
        <transport clientCredentialType="Certificate"/>
      </security>
    </binding>
  </wsHttpBinding>

Behaviors: 行为:

    <serviceBehaviors>
      <behavior name="">
      <!--
      <serviceCredentials>
        <serviceCertificate
           findValue="system"
           storeLocation="LocalMachine"
           storeName="My"
           x509FindType="FindBySubjectName"/>
      </serviceCredentials>
      -->
      <serviceAuthorization
         serviceAuthorizationManagerType=
              "ClientAuthorization.ClientCertificateAuthorizationManager, Simulator.Service.SideA" />
    </behavior>
  </serviceBehaviors>

Client 客户

Bindings: 绑定:

  <wsHttpBinding>
    <binding name="CustomBinding">      
      <security mode="Transport">
        <transport clientCredentialType="Certificate"/>
      </security>
    </binding>
  </wsHttpBinding>

Behaviors 行为

  <endpointBehaviors>
    <behavior name="ChannelManagerBehavior">
      <clientCredentials>
         <clientCertificate findValue="client1"
                           storeLocation="LocalMachine"
                           storeName="My"
                           x509FindType="FindBySubjectName" />
        <!--
        <serviceCertificate>
          <authentication certificateValidationMode="PeerOrChainTrust"/>
        </serviceCertificate>
        -->
      </clientCredentials>
     </behavior>
  </endpointBehaviors>

UPDATE UPDATE

So I added a custom username and password validator to the server in an attempt to override the default behavior and always allow regardless of the credentials presented (again I really don't want username/password validation). 因此,我向服务器添加了一个自定义用户名和密码验证器,试图覆盖默认行为,并始终允许无论提供的凭据(再次我真的不想要用户名/密码验证)。 This validator never gets invoked. 永远不会调用此验证程序。 The Client still gets the "authentication scheme 'Anonymous'.” exception. 客户端仍然获得“身份验证方案'匿名'。”例外。

Service Behavior Update 服务行为更新

  <serviceCredentials>
    <userNameAuthentication 
      userNamePasswordValidationMode="Custom"
      customUserNamePasswordValidatorType=
        "Service.ClientAuthorization.ClientUserNamePasswordValidatorManager, Service.SideA" />
  </serviceCredentials>

here is the demo for your reference. 这是演示供您参考。 i tested it under win7 + vs2010 + client-server-on-same-machine. 我在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 自创CA.

makecert -n "CN=RootCA" -r -sv c:\\rootca.pvk c:\\rootca.cer 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 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. 请注意,上面的CN值应与服务地址的DNS部分匹配。 eg hp-laptop is my computer name. 例如,hp-laptop是我的电脑名称。 the address of service endpoint will be " https://google.com :/...". 服务端点的地址为“ https://google.com:/ ...”。 (replace google dot com with 'hp-laptop' due to some stackoverflow rules). (由于某些stackoverflow规则,用'hp-laptop'替换google dot com)。

register service certificate with service program: 使用服务程序注册服务证书:

netsh http add sslcert ipport=0.0.0.0:3721 certhash=‎6c78ad6480d62f5f460f17f70ef9660076872326 appid={a0327398-4069-4d2d-83c0-a0d5e6cc71b5} 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). certhash值是服务程序证书的指纹(使用证书控制台检查)。 the appid is the GUID from service program file "AssemblyINfo.cs". appid是服务程序文件“AssemblyINfo.cs”中的GUID。

Certicate for client program: 客户计划认证:

makecert -n "CN=client1" -ic c:\\rootca.cer -iv c:\\rootca.pvk -sr LocalMachine -ss My -pe -sky exchange 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://support.microsoft.com/kb/2464556

http://blog.codit.eu/post/2013/04/03/Troubleshooting-SSL-client-certificate-issue-on-IIS.aspx 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. 另一个选择是监视实际的交换,包括ssl v3握手。 I ran into something similar and eventually discovered that the SSL Handshake was failing because I had too many trusted certificate authorities. 我碰到了类似的东西,最终发现SSL Handshake失败了,因为我有太多可信任的证书颁发机构。 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; 该解决方案使Windows的SSL部分不会将受信任的证书颁发机构发送回客户端; thus, allowing the handshake to complete. 因此,允许握手完成。

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 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加密提供程序“-sy 12 Client1.cer

So just use a 2 for the -eku parameter instead: 1.3.6.1.5.5.7.3.2 因此,只需使用2作为-eku参数: 1.3.6.1.5.5.7.3.2

The SSL handshake must complete successfully before any HTTP traffic can go through. 在任何HTTP流量通过之前,SSL握手必须成功完成。 The fact that you are getting an HTTP response at all indicates that your SSL setup is working fine. 您收到HTTP响应的事实表明您的SSL设置正常。

The 403 Forbidden response indicates that the server is set up to require HTTP basic auth username and password before providing the resource/page. 403 Forbidden响应表示服务器设置为在提供资源/页面之前需要HTTP基本身份验证用户名和密码。 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. 您需要您的客户端在HTTP级别提供基本的身份验证用户名/密码,以及您在TLS / SSL级别上已经执行的操作,或者您需要设置服务器以允许在没有HTTP基本身份验证的情况下进行访问。

I faced the same issue (Forbidden 403) for my client trying to connect to server using MutualAuthentication. 我遇到了同样的问题(Forbidden 403),我的客户端试图使用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. 确实,SSL层不是问题,同时服务器没有得到请求,因为它被传输层阻止了。 But I was facing this for 2012 server only where-as 2008 would work just fine. 但是我在2012年的服务器面临着这个问题 - 因为2008年的工作会很好。 Enabling the serviceSecurityAudit at the Service configuration via 通过服务配置启用serviceSecurityAudit

<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. 将Windows事件日志应用程序端的错误/故障显示为消息身份验证和服务授权错误。

The solution which worked for me was to introduce a custom certificate validator for clientCertificate via 对我有用的解决方案是为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 谢谢--Kirti Kunal Shah

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM