具有相互SSL的自托管WCF服务(在服务和客户端之间)失败,403 Forbidden [英] Self-Hosted WCF Service with Mutual SSL (between Service and Client) fails with 403 Forbidden

查看:127
本文介绍了具有相互SSL的自托管WCF服务(在服务和客户端之间)失败,403 Forbidden的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在自托管 WCF服务和客户端应用程序(现在的命令提示符)之​​间设置Mutual SSL演示。最后,我正试图找到一个解决方案,我在使用证书进行传入连接的服务器和多个客户端之间有传输安全性(而不是消息安全性),每个客户端都有我可以使用的单独证书用于唯一地标识每个客户。



我尝试了许多不同的方法,但没有一个有效(我无法找到一个确切的例子我一直试图做的事情)。每次我认为我越来越近,当我尝试调用服务时,我最终在客户端出现异常。我遇到的最常见的例外是:

 HTTP请求被禁止使用客户端身份验证方案'Anonymous'。 
内部异常:远程服务器返回错误:(403)禁止。

有没有人对我可能做错了什么或者更好地了解如何做在上面的场景中设置了相互SSL?



完全公开 - 截至目前我在同一台计算机上运行客户端和服务器。不确定是否重要。



下面的配置片段



服务和客户端代码相对微不足道所以我'我非常自信我已经让他们上班了。应用程序配置(特别是绑定和行为)和证书更有趣,所以我不那么自信。



我是如何创建证书的(逐字实际命令) )

  makecert -pe -nCN = SelfSignedCA-ss Root -sr LocalMachine -a sha1 -sky signature -r  - svSelfSignedCA.cerSelfSignedCA.pvk
makecert -pe -nCN = 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 -spMicrosoft RSA SChannel Cryptographic Provider-sy 12 Service.cer
makecert -pe -nCN = client1-ss my -sr LocalMachine -a sha1 -sky exchange -eku 1.3.6.1.5.5.7.3.1 -inSelfSignedCA-is Root -ir LocalMachine -spMicrosoft RSA SChannel Cryptographic Provider-sy 12 Client1.cer

将证书与港口相关联(逐字实际命令)

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

服务器设置



绑定:

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

行为:

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

客户



绑定:

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

行为

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

更新



<因此,我向服务器添加了一个自定义用户名和密码验证器,试图覆盖默认行为,并始终允许不管所呈现的凭据(再次我真的不想要用户名/密码验证)。 永远不会调用此验证程序。客户端仍然会获得身份验证方案'匿名'。异常。



服务行为更新

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


解决方案

这是一个供你参考的演示。我在win7 + vs2010 + client-server-on-same-machine下进行了测试。



服务器端:

  [ServiceContract(Name =CalculatorService)] 
公共接口ICalculatorService {
[OperationContract]
int Add(int x,int y);
}

public class CalculatorService:ICalculatorService {
public Int32 Add(I nt32 x,Int32 y){
Console.WriteLine({0}:服务方法调用(x = {1},y = {2}),
Thread.CurrentThread.ManagedThreadId,x, Y);
返回x + y;
}
}

类程序{
static void Main(string [] args){
ServicePointManager.ServerCertificateValidationCallback + =
(发送者) ,certificate,chain,sslPolicyErrors)=>真正;

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

<?xml version =1.0encoding =utf-8?> <结构>
< 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> < /结构>

客户方:

  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.0encoding =utf-8?>
< configuration>
< system.serviceModel>
< bindings>
< wsHttpBinding>
< binding name =transportSecurity>
< security mode =Transport>
< transport clientCredentialType =Certificate/>
< / security>
< / binding>
< / wsHttpBinding>
< / bindings>
< behavior>
< endpointBehaviors>
< behavior name =defaultClientCertificate>
< clientCredentials>
< clientCertificate
storeLocation =LocalMachine
storeName =My
x509FindType =FindBySubjectName
findValue =client1/>
< / clientCredentials>
< / behavior>
< / endpointBehaviors>
< / behavior>
< client>
< endpoint name =calculatorservicebehaviorConfiguration =defaultClientCertificate
address =https:// hp-laptop:3721 / calculatorservice
binding =wsHttpBinding
bindingConfiguration =transportSecurity
contract =Contract.ICalculatorService/>
< / client>
< /system.serviceModel>
< / configuration>

证书创建:



自创CA


makecert -nCN = RootCA-r -sv c:\ rootca.pvk c:\ rootca.cer


创建后,通过证书控制台将此证书导入受信任的根证书。这是停止您提到的例外的步骤。



服务计划证书


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


请注意,上面的CN值应与服务地址的DNS部分匹配。例如惠普笔记本电脑是我的电脑名称。服务端点的地址为 https://google.com :/ ...。 (由于某些stackoverflow规则,用'hp-laptop'替换google dot com。)



使用服务程序注册服务证书:


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


certhash值是服务程序证书的指纹(请与证书控制台一起检查)。 appid是来自服务程序文件AssemblyINfo.cs的GUID。



客户端程序的证书:


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


更新:根据伤寒对此解决方案的经验,由于该服务器中有太多受信任的根权限,匿名例外仍然存在。伤寒提供了两个链接来解决这个问题。



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



http://blog.codit.eu/post/2013/04/03/Troubleshooting-SSL-客户端证书问题在IIS.aspx上发布


I’m trying to setup a demo of Mutual SSL between a self-hosted WCF service and a client app (command prompt for now). 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?

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

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.

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

这篇关于具有相互SSL的自托管WCF服务(在服务和客户端之间)失败,403 Forbidden的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆