为什么我的HelloWorld WCF服务自定义X.509身份验证不起作用?

时间:2010-07-29 17:24:12

标签: c# wcf x509 custom-authentication

编辑:当我使用客户端执行测试服务时,我得到了响应。我不应该得到回应。应该发生什么是CertificateValidator.Validate()应该抛出异常,因为没有发送证书。我知道它没有被调用,因为如果我让它在没有进行任何测试的情况下抛出异常,它仍然没有。所以,我很肯定配置错误

编辑:我认为这篇文章中缺少一些代码,来自XML文件。不知道如何获取它。重要的是身份验证的配置。

这里有很多代码但是这里有。首先解释一下,我有一个有效的客户端。我的服务很有效。现在我想要一些自定义身份验证工作。我不明白为什么不是,当我测试它时应该有错误或没有响应,因为没有发送证书,但没有哪个是错误的。

编辑:抱歉,要添加,有对所有必需文件的引用等。

以下是该服务的配置。引用自定义类的位在底部,看起来几乎与MSDN站点上的相同。

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

<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
    <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
    <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere" />
      <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
      <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
      <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
    </sectionGroup>
  </sectionGroup>
</sectionGroup>
</configSections>
<appSettings/>
<connectionStrings/>
 <system.web>
  <compilation debug="true">
  <assemblies>
    <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
    <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
    <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
  </assemblies>
 </compilation>
 <!--
    The <authentication> section enables configuration 
    of the security authentication mode used by 
    ASP.NET to identify an incoming user. 
-->
<authentication mode="Windows" />
<!--
    The <customErrors> section enables configuration 
    of what to do if/when an unhandled error occurs 
    during the execution of a request. Specifically, 
    it enables developers to configure html error pages 
    to be displayed in place of a error stack trace.

    <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
     <error statusCode="403" redirect="NoAccess.htm" />
     <error statusCode="404" redirect="FileNotFound.htm" />
    </customErrors>
-->
<pages>
  <controls>
    <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  </controls>
</pages>

<httpHandlers>
  <remove verb="*" path="*.asmx"/>
  <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
</httpHandlers>
<httpModules>
  <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
</system.web>
<system.codedom>
<compilers>
  <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4"
            type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <providerOption name="CompilerVersion" value="v3.5"/>
    <providerOption name="WarnAsError" value="false"/>
  </compiler>
</compilers>
</system.codedom>
 <!-- 
  The system.webServer section is required for running ASP.NET AJAX under Internet
  Information Services 7.0.  It is not necessary for previous version of IIS.
 -->
 <system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
  <remove name="ScriptModule" />
  <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
 <handlers>
  <remove name="WebServiceHandlerFactory-Integrated"/>
  <remove name="ScriptHandlerFactory" />
  <remove name="ScriptHandlerFactoryAppServices" />
  <remove name="ScriptResource" />
  <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
       type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode"
       type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</handlers>
</system.webServer>
<runtime>
<assemblyBinding appliesTo="v2.0.05727" xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
    <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
    <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
  </dependentAssembly>
  <dependentAssembly>
    <assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
    <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
  </dependentAssembly>
  </assemblyBinding>
 </runtime>
 <system.serviceModel>
 <services>
  <service behaviorConfiguration="HelloWorldWCF2.Service1Behavior"
    name="HelloWorldWCF2.HelloWorld">
    <endpoint address="" binding="wsHttpBinding" contract="HelloWorldWCF2.IHelloWorld">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="HelloWorldWCF2.Service1Behavior">
      <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
      <serviceMetadata httpGetEnabled="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>
  </serviceBehaviors>

    <endpointBehaviors>
        <behavior name="HelloWorldWCF2.HelloWorldBehaviour">
            <clientCredentials>
                <serviceCertificate>
                    <authentication certificateValidationMode="Custom"
                           customCertificateValidatorType="CertificateValidator.X509Validator, client"/>
                </serviceCertificate>
            </clientCredentials>
        </behavior>
    </endpointBehaviors>

</behaviors>
  </system.serviceModel>
</configuration>

接下来是我的服务类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace HelloWorldWCF2
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the     interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IHelloWorld
{

    [OperationContract]
    string GetData();

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);

    // TODO: Add your service operations here
}


// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
    bool boolValue = true;
    string stringValue = "Hello ";

    [DataMember]
    public bool BoolValue
    {
        get { return boolValue; }
        set { boolValue = value; }
    }

    [DataMember]
    public string StringValue
    {
        get { return stringValue; }
        set { stringValue = value; }
    }
  }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using BusinessLogic;
using CertificateValidator;

namespace HelloWorldWCF2
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class     name "Service1" in code, svc and config file together.
public class HelloWorld : IHelloWorld
{
    CertificateValidator.X509Validator X509Val = new CertificateValidator.X509Validator();
    public string GetData()
    {
        return new Hello().hello();
    }

    public CompositeType GetDataUsingDataContract(CompositeType composite)
    {
        if (composite == null)
        {
            throw new ArgumentNullException("composite");
        }
        if (composite.BoolValue)
        {
            composite.StringValue += "Suffix";
        }
        return composite;
    }
 }
}

最后是我的CertificateValidation类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;

namespace CertificateValidator
{
  public class X509Validator : X509CertificateValidator
{
    private string[] arrThumbPrints = new string[2];
    protected string[] GetTrustedThumbprints()
    {
        return arrThumbPrints;
    }

        public override void Validate(X509Certificate2 certificate)
        {
            // create chain and set validation options
            X509Chain chain = new X509Chain();
            SetValidationSettings(chain);

            // check if cert is valid and chains up to a trusted CA
            if (!chain.Build(certificate))
            {
                throw new SecurityTokenValidationException("Client certificate is not valid");
            }

            // check if cert is from our trusted list
            if (!IsTrusted(chain, GetTrustedThumbprints()))
            {
                throw new SecurityTokenValidationException("Client certificate is not trusted");
            }
        }

        protected virtual void SetValidationSettings(X509Chain chain)
        {
            // override to use non-default validation settings
        }

        protected virtual ValidationMode ValidationMode
        {
            get { return ValidationMode.Issuer; }
        }

        protected virtual bool IsTrusted(X509Chain chain, string[] trustedThumbprints)
        {
            int depth = 0;

            if (ValidationMode == ValidationMode.EndCertificate)
            {
                // only check the end certificate
                return CheckThumbprint(chain.ChainElements[0].Certificate, trustedThumbprints);
            }
            else
            {
                // check the rest of the chain
                foreach (X509ChainElement element in chain.ChainElements)
                {
                    if (++depth == 1)
                    {
                        continue;
                    }

                    if (CheckThumbprint(element.Certificate, trustedThumbprints))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private bool CheckThumbprint(X509Certificate2 certificate, string[] trustedThumbprints)
        {
            foreach (string thumbprint in trustedThumbprints)
            {
                if (string.Equals(certificate.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }

        protected void DumpDiagnostics(X509Chain chain, X509Certificate2 certificate)
        {
            Console.WriteLine("Subject: {0}", certificate.Subject);
            Console.WriteLine("Issuer : {0}", certificate.Issuer);

            Console.WriteLine("Status: {0}", chain.ChainStatus.Length);
            foreach (X509ChainStatus status in chain.ChainStatus)
            {
                Console.WriteLine("{0} \\ {1}",
                    status.Status,
                    status.StatusInformation);
            }

            foreach (X509ChainElement element in chain.ChainElements)
            {
                Console.WriteLine(element.Information);
            }
        }
    }
  }

1 个答案:

答案 0 :(得分:0)

我在配置中看到两个问题。

  1. 您正在使用默认的WSHttpBinding。默认设置使用Windows凭据的邮件安全性。此安全设置不需要证书,因此根本不使用它们。您必须定义新的WSHttpBinding,并将客户端凭据类型设置为certificate,并使用endpointConfiguration属性在端点中引用该设置。
  2. 我不确定您要验证哪个证书。您的配置描述了服务器证书的验证,该证书应在客户端上用于验证协商的服务凭证,但您在服务配置中使用它=它将不会被使用。您必须定义服务行为并使用serviceCredentials \ clientCertificate \ authentication来指定客户端证书的验证。
  3. 祝你好运, 拉吉斯拉夫