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

发布于 2024-09-12 00:45:51 字数 15097 浏览 3 评论 0原文

编辑:当我与客户端执行测试服务时,我得到了响应。我不应该得到回应。应该发生的是,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);
            }
        }
    }
  }

Edit: When I execute test the service with a client I get a response. I should not get a response. What should happen is CertificateValidator.Validate() should throw an exception because there is no certificate being sent. I KNOW it is not being called because if I get it to throw an exception without making any tests it still doesn't. So, I am positive the configuration is wrong

EDIT: I think some code is missing in this post, from the XML file. Don't know how to get it in. Importantly is the config for the authentication which is in there.

There is a lot of code here but here goes. First an explanation, I have a client which works. My service works. Now I want to get some custom authentication working. I don't see why it isn't, when I test it there should be an error or no response because a certificate is not being sent, but there isn't which is wrong.

edit: Sorry, to add, there are references to all the required files etc.

Here is the configuration for the service. The bit for the reference to the custom class is at the bottom, it looks nearly identical to what is on the MSDN site.

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

Next are my service classes:

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; }
    }
  }
}

And:

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;
    }
 }
}

Finally is my CertificateValidation class:

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);
            }
        }
    }
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清眉祭 2024-09-19 00:45:51

我发现您的配置有两个问题。

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

此致,
拉迪斯拉夫

I see two problems in your configuration.

  1. You are using default WSHttpBinding. Default setting uses message security with Windows credentials. This security setting does not require certificates so it doesn't use them at all. You have to define new WSHttpBinding with client credential type set to certificate and reference that setting in your endpoint by using endpointConfiguration attribute.
  2. I'm not sure which certificate are you going to validate. Your configuration describes validation of server certificate which should be used on a client to validate negotiated service credentials but you are using it in the service configuration = it will not be used. You have to define service behavior and use serviceCredentials\clientCertificate\authentication to specify validation of client certificate.

Best regards,
Ladislav

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文