WCF服务中的自定义客户端证书和用户名验证

Han*_*kun 7 wcf client certificate username

我的特殊问题是这样的:

  • 我们目前正在运行一组服务,要求客户端在调用服务时提供用户名和密码作为身份验证.

  • 我们希望在这些服务上实施PKI基础设施,但我们的一些合作伙伴将比其他合作伙伴使用更长的时间来适应这种新的基础设施.

  • 作为第一步,我们希望从我们的一些合作伙伴那里获得客户证书.将需要客户端证书(除了用户名和密码)来访问我们服务器上的数据,而对于其他用户,只需要用户名和密码.

为了解决这个问题,我试图在WCF中为用户名/密码认证(使用UserNamePasswordValidator)和客户端证书(使用X509CertificateValidator)实现自定义验证器.用户名/密码验证程序将验证这些凭据是否符合我们的数据库,而客户端证书验证程序将检查请求是否来自我们需要证书的客户端,如果是,则验证是否提供了有效的客户端证书.我无法配置WCF以便它使用这两个验证器.

我在服务器上的WCF配置目前设置如下:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpsGetEnabled="true" policyVersion="Policy15" />
      <serviceDebug includeExceptionDetailInFaults="true" />
      <serviceCredentials>
        <clientCertificate>
          <authentication customCertificateValidatorType="MyWS.Security.MyServicesCertificateValidator, MyWS"
            certificateValidationMode="Custom" revocationMode="NoCheck" />
        </clientCertificate>
        <userNameAuthentication userNamePasswordValidationMode="Custom"
          customUserNamePasswordValidatorType="MyWS.Security.MyServicesUsernameValidator, MyWS" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<bindings>
  <basicHttpBinding>
    <binding name="MySoapBinding">
      <security mode="TransportWithMessageCredential">
        <transport clientCredentialType="Certificate" />
        <message clientCredentialType="UserName" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<services>
  <service behaviorConfiguration="MyServiceBehavior" name="MyWS.Services.TheService">
    <endpoint address="" binding="basicHttpBinding" bindingConfiguration="MySoapBinding" name="TheService" bindingNamespace="https://services.my/TheService" contract="MyWS.Interfaces.Service.ITheService" />
    <host>
      <baseAddresses>
        <add baseAddress="https://localhost:4434/MyWS/TheService"/>
      </baseAddresses>
    </host>
  </service>
</services>
Run Code Online (Sandbox Code Playgroud)

据我所知,此配置无效,因为我无法在传输层使用customCertificateValidatorType(因为IIS在此处涉及WCF之前检查证书),但我无法看到我如何将customCertificateValidatorType和customUserNamePasswordValidatorType组合为凭据消息层的类型也是.

我已经实现了一个消息检查器,并且可能能够以某种方式使用OperationContext来解决问题(如下面链接中所示),但是我还没有看到这样做的方式......

http://social.msdn.microsoft.com/Forums/en/wcf/thread/b6ab8b58-516b-41d4-bb0e-75b4baf92716

我想我可能会尝试实现与WCF工作方式不兼容的东西,但如果有人知道如何解决这个问题,我很乐意收到你的反馈意见!

Han*_*kun 6

我想我已经找到了解决问题的方法,感谢@ ladislav-mrnka在他的回答中提供的宝贵意见.我意识到有必要提供两个端点来配置不同的需求,我还了解了配置服务时支持令牌的可能性.

我在MSDN上找到了一个关于支持令牌的链接,按照这个配方我已经在服务器上使用以下自定义绑定实现了端点(我通过代码切换到配置.不确定是否可以在web.config中设置它.)

private static Binding CreateMultiFactorAuthenticationBinding()
{
    var httpsTransport = new HttpsTransportBindingElement();

    // The message security binding element will be configured to require 2 tokens:
    // 1) A username-password encrypted with the service token
    // 2) A client certificate used to sign the message

    // Create symmetric security binding element with encrypted username-password token.
    // Symmetric key is encrypted with server certificate.
    var messageSecurity = SecurityBindingElement.CreateUserNameForCertificateBindingElement();
    messageSecurity.AllowInsecureTransport = false;

    // Require client certificate as endorsing supporting token for all requests from client to server
    var clientX509SupportingTokenParameters = new X509SecurityTokenParameters
                                                    {
                                                        InclusionMode =
                                                            SecurityTokenInclusionMode.AlwaysToRecipient
                                                    };
    messageSecurity.EndpointSupportingTokenParameters.Endorsing.Add(clientX509SupportingTokenParameters);

    return new CustomBinding(messageSecurity, httpsTransport);
}
Run Code Online (Sandbox Code Playgroud)

此绑定创建SymmetricSecurityBindingElement,其中对称密钥(使用服务器证书加密)用于加密邮件头中的用户名/密码安全令牌以及邮件正文本身.

此外,还添加了X509安全令牌作为支持,支持绑定令牌.此令牌配置为始终包含在客户端对服务器的请求中.

此自定义绑定随后用于配置具有需要此绑定的端点的新WCF服务.我在Castle Windsor使用WcfFacility来配置服务.

此代码执行以下操作:

  • 设置服务证书
  • 将客户端证书的验证模式设置为链信任,以便传入的客户端证书必须由服务器存储中的受信任根证书颁发机构颁发
  • 为用户名/密码凭据和客户端证书添加自定义验证器
//// Registering WCF-services
var returnFaults = new ServiceDebugBehavior {IncludeExceptionDetailInFaults = true};
var metaData = new ServiceMetadataBehavior {HttpsGetEnabled = true};

var serviceCredentials = new ServiceCredentials();

// Configure service sertificate
serviceCredentials.ServiceCertificate.SetCertificate(
    StoreLocation.LocalMachine, 
    StoreName.My, 
    X509FindType.FindBySubjectName,
    "ServerCertificate");

// Configure client certificate authentication mode
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;

// Add custom username-password validator
serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode =
    UserNamePasswordValidationMode.Custom;
serviceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator =
    _container.Resolve<MyServicesUsernameValidator>();

// Add custom certificate validator
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode =
    X509CertificateValidationMode.Custom;
serviceCredentials.ClientCertificate.Authentication.CustomCertificateValidator =
    _container.Resolve<MyServicesCertificateValidator>();

var serviceModel = new DefaultServiceModel();

serviceModel.AddEndpoints(
    WcfEndpoint.ForContract<IMyContract>().BoundTo(CreateMultiFactorAuthenticationBinding()));
serviceModel.BaseAddresses.Add(new Uri("https://server.com/MyServiceImplementation.svc"));

serviceModel.AddExtensions(serviceCredentials);
serviceModel.AddExtensions(metaData);

_container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
    .Register(Component.For<IMyContract>()
                    .ImplementedBy<MyServiceImplementation>()
                    .AsWcfService(serviceModel),
                Component.For<IServiceBehavior>().Instance(returnFaults));
Run Code Online (Sandbox Code Playgroud)

MyServicesUsernameValidator继承UserNamePasswordValidator,MyServicesCertificateValidator继承X509CertificateValidator.两者都会覆盖其相应的Validate方法.

这似乎解决了我的特殊问题...希望它能解决你的问题!:)