4

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

我已经尝试了许多不同的方法,但都没有奏效(我无法找到我一直在尝试做的确切示例)。每次我认为我已经接近时,当我尝试调用服务时,我都会在客户端出现异常。我遇到的最常见的例外是:

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

有没有人对我可能做错了什么有任何想法,或者可能更好地了解如何在上述情况下设置相互 SSL?

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

下面的配置片段

服务和客户端代码相对简单,所以我非常有信心让它们工作。应用程序配置(特别是绑定和行为)和证书“更有趣”,所以我不那么自信。

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

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

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

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>
4

5 回答 5

3

另一种选择是监控实际的交换,包括 ssl v3 握手。我遇到了类似的事情,最终发现 SSL 握手失败了,因为我有太多受信任的证书颁发机构。为了解决这个问题,我需要遵循这个决议。该解决方案阻止 Windows 的 SSL 部分将受信任的证书颁发机构发送回客户端;因此,允许握手完成。

于 2014-04-24T04:06:07.887 回答
3

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

服务器端:

[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>

客户端:

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>

证书创建:

自创CA

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

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

服务计划证书

makecert -n "CN=hp-laptop" -ic c:\rootca.cer -iv c:\rootca.pvk -sr LocalMachine -ss 我的 -pe -sky 交换

请注意,上面的 CN 值应与服务地址的 DNS 部分匹配。例如 hp-laptop 是我的计算机名称。服务端点的地址将是“ https://google.com :/...”。(由于某些 stackoverflow 规则,将 google dot com 替换为 'hp-laptop')。

向服务程序注册服务证书:

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

certhash 值是服务程序证书的指纹(使用证书控制台检查)。appid 是服务程序文件“AssemblyINfo.cs”中的 GUID。

客户程序证书:

makecert -n "CN=client1" -ic c:\rootca.cer -iv c:\rootca.pvk -sr LocalMachine -ss 我的 -pe -sky 交换

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

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

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

于 2014-04-20T15:07:47.823 回答
2

我有几天同样的问题,直到最后,我意识到这一行创建了一个“服务器身份验证”证书。您需要“客户端身份验证”证书,否则客户端证书将被视为无效。

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

所以只需使用2作为-eku参数:1.3.6.1.5.5.7.3.2

于 2015-06-11T18:09:34.533 回答
0

SSL 握手必须成功完成,才能通过任何 HTTP 流量。您收到 HTTP 响应的事实表明您的 SSL 设置工作正常。

403 Forbidden 响应表明服务器设置为在提供资源/页面之前需要 HTTP 基本身份验证用户名和密码。除了您已经在 TLS/SSL 级别执行的操作之外,您还需要您的客户端在 HTTP 级别提供基本身份验证用户名/密码,或者您需要设置服务器以允许在没有 HTTP 基本身份验证的情况下进行访问。

于 2014-04-20T02:15:07.967 回答
0

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

于 2015-06-25T06:54:15.420 回答