【问题标题】:WCF Security Authentication with https and validation使用 https 和验证的 WCF 安全身份验证
【发布时间】:2014-11-28 12:48:58
【问题描述】:

我试图了解 WCF 中的身份验证是如何工作的。有一个带有 https 端点的自托管服务,它绑定到我的一个证书。

时一切正常

security mode="None"

那么即使在不同的机器上也能正常工作。与

的情况相同

mode="Transport" and <transport clientCredentialType="None"/>

。当我尝试通过 UserName 添加验证时,它工作正常,但仅在本地主机上,当托管在不同的机器上时,我收到一个错误:

“从其他人那里收到了一个不安全的或不正确的安全故障 派对……”

为什么它不适用于用户名密码验证?

EDIT2:我发现服务器上抛出了以下异常并被捕获(但仅在不同的机器上):“安全时间戳无效,因为它的创建时间('2014-11-29T01:30:48.824Z')是在未来。当前时间是“2014-11-28T14:51:52.704Z”,允许的时钟偏差是“00:05:00”。”。我不知道这个创建时间在哪里,但肯定不是来自另一台机器。会发生什么?

第二个问题是关于证书的。托管 https 并将地址绑定到证书时,是否应将此证书作为受信任的方式安装在客户端计算机上? (我用 netsh 绑定它) 如果没有以下代码,我将无法连接到我的 wcf 服务:

  System.Net.ServicePointManager.ServerCertificateValidationCallback =
  ((sender, certificate, chain, sslPolicyErrors) => true);

是在客户端进行证书验证吗?它检查它是否存在以及它是否由受信任的发行者发行?

编辑: 附加问题: 当我尝试使用浏览器输入服务安全端点时,它说此连接不安全,不可信来源等。在我的服务机器上,我已将证书绑定到 https,并且该证书是由“CertificateIssuer”颁发的 MyCertificate。现在我将颁发者证书安装到服务和客户端机器中 - 我的意思是“CertificateIssuer”进入受信任的根证书颁发机构”,当我从同一台机器进入时我仍然不是受信任的事件。我应该如何将它配置为受信任?

服务器配置:

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

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
  </appSettings>
  <system.web>
    <compilation debug="true"/>
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.diagnostics>
    <trace autoflush="true"/>
    <sources>
      <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="sdt" type="System.Diagnostics.XmlWriterTraceListener" initializeData="SdrConfigExample.e2e"/>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <system.serviceModel>
    <services>
      <service name="WcfService1.TestService" behaviorConfiguration="ServiceUsernameValidation">
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:8734/Services/"/>
            <add baseAddress="http://localhost:8735/Wsdl/"/>
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address="Test" binding="basicHttpBinding" contract="WcfService1.ITestService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
        </endpoint>

        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the values below to false before deployment -->
          <serviceMetadata httpsGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
        <behavior name="ServiceUsernameValidation">
          <serviceMetadata httpsGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfSecurity.PasswordValidator,WcfSecurity"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>

    </behaviors>

    <bindings>
      <basicHttpBinding>
        <binding>
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>

  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

客户端配置:

<?xml version="1.0"?>
<configuration>
  <system.diagnostics>
    <trace autoflush="true"/>
    <sources>
      <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="sdt" type="System.Diagnostics.XmlWriterTraceListener" initializeData="SdrConfigExample.e2e"/>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_ITestService">
          <security mode="TransportWithMessageCredential" >
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://175.28.233.153:8734/Services/Test" binding="basicHttpBinding"
        bindingConfiguration="BasicHttpBinding_ITestService" contract="ServiceReference1.ITestService"
        name="BasicHttpBinding_ITestService" />
    </client>
  </system.serviceModel>
</configuration>

服务器代码:

 ServiceHost host = new ServiceHost(typeof(TestService));


            var uri = host.Description.Endpoints[0].Address.Uri;
            var cert = FindCertificate("CN=MyCertificate");
            if (!ReserveAddressForHttps(uri.Host, uri.Port, cert, 5000))
            {
                throw new Exception("Failed to assign service certificate into local interface.");
            }
            host.Open();

            host.Description.Endpoints.ToList().ForEach(x => Console.WriteLine(x.Address));
            Console.WriteLine();
            Console.WriteLine("Service started...");
            Console.ReadLine();
            host.Close();

客户端代码:

 TestServiceClient client = new TestServiceClient();

        client.ClientCredentials.UserName.UserName = "hej";//this is valid
        client.ClientCredentials.UserName.Password = "hej"; 
            System.Net.ServicePointManager.ServerCertificateValidationCallback =
  ((sender, certificate, chain, sslPolicyErrors) => true);
            try
            {
                var result = client.GetData(123);
                Console.WriteLine(result);

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            Console.ReadLine();

【问题讨论】:

    标签: c# wcf security ssl


    【解决方案1】:

    安全时间戳的目的是guard against "Replay Attacks"

    在您的情况下,创建日期(在客户端上设置)比服务器上的日期晚近 12 小时。最明显的解释是其中一个时钟设置不正确。

    重放攻击检测can be disabled,不像requests have to be signed anyway to benefit from it那样有安全风险。

    关于证书,安装过程为documented on TechNet - 注意可以查看证书是否安装正确:

    如果您想验证证书是否已安装,您可以加载 证书卡入,您应该在证书下看到它 –当前用户信任的根证书颁发机构-证书。

    一旦您的浏览器信任该证书,您的 WCF 客户端也会信任该证书。

    【讨论】:

    • 感谢您的回复。我改变时间后它起作用了。我还有一个问题:我使用 TransportWithMessageCredential 模式验证用户名。服务器托管在一些未安装在客户端的证书下。如果客户端没有私钥来解密消息,它怎么能工作?也许有一些关于托管 https 服务器的东西我不明白,但我认为证书 - 用于托管 https 服务器应该安装在客户端计算机上以提供私钥。我错了吗?
    • @Zbigniew 服务器证书不需要安装在客户端,但客户端必须信任服务器证书(或配置为不关心)。有一个很棒的explanation of how it works on ISSE
    猜你喜欢
    • 2012-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 2014-07-28
    • 2016-05-21
    相关资源
    最近更新 更多