Use C2WTS to get a classic windows identity from a claims identity

Posted on 11/6/2013 @ 11:20 PM in #SharePoint by | Feedback | 5811 views

I know you’re going to find this useful at some point. A lot of backend systems still demand classic windows identities, but everything we do now has moved to claims. So sometimes (albeit rare), we have to translate a claims identity into a classic windows identity.

This is where the “Claims to Windows Token Service” comes into play. SharePoint 2010 and 2013 make use of this but you can use this in any .NET application.

First of all, there are some basic requirements for this to work,

First, you will need the string value of a UPN claim. Just a string value, really! This means you can also use FBA or anything else. The “proper” way to do this of course is that you must originate this from a AD backed claim. So a user authenticated using ADFS or similar would be perfect. Just remember that you must issue the UPN claim.

Secondly, C2WTS (Claims to Windows Token Services) must be configured properly.

The second bit is usually a bit nutty, if you get this wrong, you’ll get the following error -

WTS0003: The caller is not authorized to access the service.

Here is how you setup C2WTS,

  1. Ensure that the “Claims to Windows Token Service” and “Cryptographic Services” services are running, and are configured to run under a domain account.
  2. That domain account, ensure it has the following permissions (how to grant these permissions? open secpol.msc, go to local policies\user rights assignment)
    1. Act as part of the OS
    2. Impersonate a client after authentication
    3. Logon as a service.
  3. Go to C:\Program Files\Windows Identity Foundation\v3.5, and open the c2wtshost.exe.config file – add this service account as an allowed caller. By default nobody is allowed to use this service, if you have SharePoint installed, sneaky SharePoint has added WSS_WPG in already as an allowed caller.

That’s it! (Note you may also have to do constrained delegation etc. in SharePoint scenarios where you wish to forward the identity to an external system .. see further detailed steps here).

So how do we go about using it?

The key method is,

Microsoft.IdentityModel.WindowsTokenService.S4UClient.UpnLogon(“upnclaimvalue”);

So for instance, the below line of code will give you a Windows Identity for “Administrator@winsmarts.internal”

   1:  windowsIdentity = S4UClient.UpnLogon(Administrator@winsmarts.internal);

Once you have the windowsIdentity, you can create a WindowsImpersonationContext, in which you can then “pretend to act as the impersonated user” .. in this case “Administrator@winsmarts.internal”. .. here is how,

   1:  using (WindowsImpersonationContext ctx = windowsIdentity.Impersonate())
   2:  {
   3:      // do work using windows identity, say connect to SQL server using a trusted connection.
   4:  }

Again, note that the string value isn’t arbitrary – it must match a real UPN claim on your domain.

The best way to get that, of course, is from a UPN claim : – )

How do we get that? Using the code below,

   1:  var upnClaim = from Microsoft.IdentityModel.Claims.Claim c in claimsIdentity.Claims
   2:           where c.ClaimType == System.IdentityModel.Claims.ClaimTypes.Upn
   3:           select c;
   4:  string upn = upnClaim.First().Value;

So how does the full code put together look like? A bit like this -

   1:  IClaimsIdentity claimsIdentity = System.Threading.Thread.CurrentPrincipal.Identity as IClaimsIdentity;
   2:  if (claimsIdentity != null)
   3:  {
   4:      if (claimsIdentity.Claims.Count > 0)
   5:      {
   6:          var upnClaim = from Microsoft.IdentityModel.Claims.Claim c in claimsIdentity.Claims
   7:                          where c.ClaimType == System.IdentityModel.Claims.ClaimTypes.Upn
   8:                          select c;
   9:          if (upnClaim.Count() > 0)
  10:          {
  11:              string upn = upnClaim.First().Value;
  12:              WindowsIdentity windowsIdentity = null;
  13:              try
  14:              {
  15:                  windowsIdentity = S4UClient.UpnLogon(upn);
  16:                  if (windowsIdentity != null)
  17:                  {
  18:                      using (WindowsImpersonationContext ctx = windowsIdentity.Impersonate())
  19:                      {
  20:                          // do work using windows identity, say connect to SQL server using a trusted connection.
  21:                      }
  22:                  }
  23:              }
  24:              catch (SecurityAccessDeniedException)
  25:              {
  26:                  // handle exception
  27:              }
  28:          }
  29:      }
  30:  }

The above code will need references to Microsoft.IdentityModel, System.IdentityModel and System.ServiceModel.

Great! Now you can go from claims identity to classic windows identity easily! FUN!

Sound off but keep it civil:

Older comments..