overview scenarios accessing claims windows authentication username authentication
I use this configuration:
<system.serviceModel>
<services>
<service name=“Common.ClaimsService“>
<endpoint address=“certificatemixed“
binding=“netHttpBinding“
contract=“Common.IClaimsService“ />
</service>
</services>
<bindings>
<netHttpBinding>
<binding>
<security mode=“TransportWithMessageCredential“>
<message clientCredentialType=“Certificate“ />
</security>
</binding>
</netHttpBinding>
</bindings>
</system.serviceModel>
On the client you need to pass in the certificate you want to use for authentication (the X509 class is from Thinktecture.IdentityModel):
var factory = new ChannelFactory<IClaimsService>(“*”);
factory.Credentials.ClientCertificate.Certificate =
X509.
CurrentUser.
My.
SubjectDistinguishedName.
Find(“CN=Client”).
First();
var proxy = factory.CreateChannel();
var id = proxy.GetIdentity();
By default the WCF service will do a chain validation check against the client cert, and if the issuer is in your trusted CA store, this will just work. You can also check against a fixed list of allowed client certificates, by doing a search if the client cert is in the Trusted People store:
<behaviors>
<serviceBehaviors>
<behavior>
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode=“PeerTrust“ />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
If you want more fine-grained control over certificate validation, you need to write your own validator logic. The following code snippet does a chain validation first, and then checks for a specific CA:
public class CertificateValidator : X509CertificateValidator
{
public override void Validate(X509Certificate2 certificate)
{
if (new X509Chain().Build(certificate))
{
if (certificate.Issuer == “CN=CorpCA”)
{
return;
}
}
throw new SecurityTokenValidationException();
}
}
Your wire up that validator in the serviceCredentials/clientCertificate config.
With this approach Thread.CurrentPrincipal will be empty, and you have to use the ServiceSecurityContext to get to the client’s identity, which will be in this case a combination of the certificate subject name and thumbprint. Don’t like that.
Enabling “WIF” Mode
In WIF mode you have access to the client certificate details via claims on Thread.Currentprincipal. But there are also different approaches when it comes to validation.
<behaviors>
<serviceBehaviors>
<behavior>
<serviceCredentials useIdentityConfiguration=“true“ />
</behavior>
</serviceBehaviors>
</behaviors>
By default, WCF will use the configured issuer name registry to find out, if you want to accept the client certificate. WCF ships with one built-in registry, called the ConfigurationBasedIssuerNameRegistry. A issuer name registry gets to see the certificate of the issuer of the client cert and can either accept the issuer, or reject it, e.g.:
<system.identityModel>
<identityConfiguration>
<issuerNameRegistry type=“….ConfigurationBasedIssuerNameRegistry, … “>
<trustedIssuers>
<add name=“CorpCA“
thumbprint=“B772884B7DC7F367CDBA0C9D7CE8348946876E96“ />
</trustedIssuers>
</issuerNameRegistry>
</identityConfiguration>
</system.identityModel>
This is how you would implement the above custom validation scenario (here or in Thinktecture.IdentityModel you can find a registry called TestIssuerNameRegistry which allows all issuers and return the subject distinguished name of the issuer).
You can write your own registry if you need that level of control. When you also need access to the details of the client cert (not just the issuer), you can still use a certificate validator (or specifically disable it). This time you have to register the validator in the <system.identityModel /> section:
<system.identityModel>
<identityConfiguration>
<certificateValidation certificateValidationMode=“Custom“>
<certificateValidator type=“Type, Assembly“/>
</certificateValidation>
</identityConfiguration>
</system.identityModel>
And after you told WCF to also populate Thread.CurrentPrincipal, everything works now as expected:
<behaviors>
<serviceBehaviors>
<behavior>
<serviceCredentials useIdentityConfiguration=“true“ />
<serviceAuthorization principalPermissionMode=“Always“ />
</behavior>
</serviceBehaviors>
</behaviors>
Hi Dominick,
excellent stuff!
The whole new model still has me somewhat baffled, especially when I try to figure out Web API at the same time.
I’m working in an interop kind of environment, with WSDL’s I get from specified elsewhere (that I have to implement the service for) using SSL and client certificate authentication.
One thing I’m now facing is the errors I get for denied certificates, when I use the above sample to check the client certificate’s issuer using the ConfigurationBasedIssuerNameRegistry.
Instead of a “403 acces denied”, I get “server error 500”, showing an InternalServiceFault. When I use ServiceBehavior.IncludeExceptionDetailInFault=true , it tells me (correctly) that my client certificate’s issuer doesn’t match the specified criteria. When I turn off the details, I get zero hint
at the client side that it’s an authentication issue. That’s not very helpful.
Ofcourse, I can implement my own IErrorHandler, or my own issuerNameRegistry , or my own CertificateValidator. Still, I’m thinking maybe I’m overlooking something. How can I possibly need a custom piece of code to just emit a basic authentication denial with some readable hint as to its cause??
Welcome to WCF ;)
Pingback: WCF and Identity in .NET 4.5: External Authentication with WS-Trust | www.leastprivilege.com
Reblogged this on Peter's ruminations and commented:
two question come to mind:
does it work when hosted in IIS? (or does IIS need further specialized configuration for the case of certs NOT stored in a cert store)?
how about something simpler. Lets say I just want to pull the metadata stream of a WIF IDP, requiring a “transport” level (SSL) client cert be presented. There is no interface, no soap, no json, no …. Again, one wants that the client cert (nor its root ) must not need to be pre-registered with the windows server instances in the pool. One JUST want a cert validator class to be used. IN this case, even a browser can pull the metadata file, and present its client cert from the usual browser cert dialog and associated smartcard.
Can you show how to set up a ServiceHost with a Certificate? I need to Self Host a WCF service and I tend to make my hosting options configurable… so, I need it in code instead of through configuration files. I am able to get the WCF Service/Client working sharing a certificate but I cannot get the ServiceHost configured to get it working. I suggest this be added to your PluralSight class as well. Thanks.
It’s a property on ServiceHost.Credentials – check the docs.