2

I'm setting up client certificates on my wcf service. All works great. The service requires client certs, my client test app supplies the cert and is able to make a request to one of the service end points.

No I want to implement a custom validator. I created a new class inheriting from X509CertificateValidator, and set it up in the services web config. I can put a breakpoint in the validate method and see it gets called. Awesome possum.

Now I want to be able to supply custom configuration parameters to the validator. The X509CertificateValidator has a LoadCustomConfiguration method which I can override, but it doesn't get called, I'm assuming it's because I'm not supplying any actual custom configuration anywhere - if that assumption is correct, how do I define my custom configuration parameters? Or is there some other way I should be doing this?

public class CustomValidator : System.IdentityModel.Selectors.X509CertificateValidator
{
    /// <summary>
    /// If the passed certificate is not valid according to the validation logic, this method throws a SecurityTokenValidationException. If the certificate is valid, the method returns to the caller.
    /// </summary>
    /// <param name="certificate"></param>
    public override void Validate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
    {
        bool bValid = true;

        // Check that there is a certificate.
        if (certificate == null)
        {
            throw new ArgumentNullException("certificate", "Certificate was not supplied.");
        }

        bValid = certificate.Verify() &&
            DateTime.Now <= certificate.NotAfter &&
            DateTime.Now >= certificate.NotBefore;

        if (!bValid)
        {
            throw new System.IdentityModel.Tokens.SecurityTokenValidationException("Certificate is not valid.");
        }
    }

    public override void LoadCustomConfiguration(System.Xml.XmlNodeList nodelist)
    {
        base.LoadCustomConfiguration(nodelist);
    }
}

Configuration

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

  <appSettings>    
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />    
  </appSettings>

  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
  </system.web>

  <system.serviceModel>

    <services>
      <service name="WCFTransportAuthCertificateCustomValidation.Service1"
               behaviorConfiguration="MapClientCertificates">
        <endpoint binding="basicHttpBinding"
                  bindingConfiguration="TransportCertificateAuthentication"
                  contract="WCFTransportAuthCertificateCustomValidation.IService1">
        </endpoint>
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="TransportCertificateAuthentication">
          <security mode="Transport">
            <transport clientCredentialType="Certificate"></transport>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>    
        <behavior>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
            <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>

        <behavior name="MapClientCertificates">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>

          <serviceCredentials>
            <clientCertificate>
              <authentication certificateValidationMode="Custom" customCertificateValidatorType="X509CertificateValidation.CustomValidator, X509CertificateValidation"  />                
            </clientCertificate>
          </serviceCredentials>
        </behavior>

      </serviceBehaviors>
    </behaviors>

    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https"/>
    </protocolMapping>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

  </system.serviceModel>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>
Jeremy
  • 44,950
  • 68
  • 206
  • 332
  • Hi Jeremy. Had exactly the same intentions and came across your post. Any idea why this isn't working? Or any idea how to pass additional attributes to the valdiator? Regards, Tom – Thomas Zweifel May 20 '15 at 14:00
  • @Thomas Zweifel - I didn't make any headway with this. The only thing I can think of is to use the ConfigurationManager so you can access the appsettings. I didn't actually try it yet, but it should work. Not as clean as I wanted though because I don't like coupling the validator to my application configuration. I think the app should be coupled to it's own config and somehow pass the parameters to the validator – Jeremy May 20 '15 at 23:02
  • 1
    Thanks. I have multiple endpoints and wanted to use a custom validator for certificate pinning for each endpoint. Each endpoint should trust an different certificate. Since the validator has no Information about the endpoint it is assigned to, using the ConfigurationManager is not possible. Therefore I ended up writing individual validator classes for each endpoint and hardcoded the pinning values in the individual validator classes. Not a very smooth approach but the only solution I found. – Thomas Zweifel May 21 '15 at 06:51

0 Answers0