Zulfiqar's weblog

Architecture, security & random .Net

Archive for the ‘Federation/STS’ Category

Configuring SAML Assertion Subject Name and Format for a WIF STS

Posted by zamd on April 27, 2010

In some interop scenarios, subject name and its format needs to be included in the Saml token/assertion generated by the STS. You can easily configure a WIF based STS to generate this by adding a NameIdentifier claim and by settings it’s format property.

protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal,

    RequestSecurityToken request, Scope scope)

{

    var nameIdentifierClaim = new Claim(ClaimTypes.NameIdentifier, "me@zamd.com");

    nameIdentifierClaim.Properties[ClaimProperties.SamlNameIdentifierFormat] = "EMAIL";

 

    return new ClaimsIdentity(

        new Claim[]

        {

            new Claim(System.IdentityModel.Claims.ClaimTypes.Name, "Zulfiqar"),

            nameIdentifierClaim

        });

 

This generates following Saml Assertion where you can see the generated NameIdentifier & format attribute.

 <saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
   <saml:Subject>
    <saml:NameIdentifier Format="EMAIL">me@zamd.com</saml:NameIdentifier>
     <saml:SubjectConfirmation>
      <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
    </saml:SubjectConfirmation>
  </saml:Subject>
   <saml:Attribute AttributeName="name" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
    <saml:AttributeValue>Zulfiqar</saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>

Posted in WIF | 1 Comment »

ExtractSAMLAssertion

Posted by zamd on March 10, 2010

Few people have asked me for the source code of ExtractSAMLAssertion helper method/class, which I have used in my article and blog posts. So here you go…

Please note this is just raw test code and doesn’t have any error checking and other related goodies.

 

    class Program

    {

        static void Main(string[] args)

        {

            var binding = new WSHttpBinding(SecurityMode.Message);

            binding.Security.Message.ClientCredentialType = MessageCredentialType.None;

            binding.Security.Message.EstablishSecurityContext = false;

 

            var fac = new WSTrustChannelFactory(binding, http://localhost:9000/STS&#8221;);

            fac.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            fac.Open();

           

            var rst = new RequestSecurityToken {

                AppliesTo = new EndpointAddress(http://localhost&#8221;),

                RequestType = WSTrustFeb2005Constants.RequestTypes.Issue

            };

 

            var token = fac.CreateChannel().Issue(rst) as GenericXmlSecurityToken;

            var rpCert = new X509Certificate2(“localhost.pfx”, “a”);

            var xml = ExtractSAMLAssertion(token, rpCert);

        }

   

        private static string ExtractSAMLAssertion(GenericXmlSecurityToken token, X509Certificate2 rpCert)

        {

            string txtAssertion = string.Empty;

            var handlerCol = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();

            var han = handlerCol.OfType<EncryptedSecurityTokenHandler>().FirstOrDefault();

            if (han != null)

            {

                han.Configuration.ServiceTokenResolver = new EncryptedKeyResolver(rpCert);

                var sr = new StringReader(token.TokenXml.OuterXml);

                var rdr2 = XmlReader.Create(sr);

                var rtok = han.ReadToken(rdr2) as SamlSecurityToken;

 

                var stat = rtok.Assertion.Statements[0];

                var ms = new MemoryStream();

                var memWriter = XmlDictionaryWriter.CreateTextWriter(ms);

                stat.WriteXml(memWriter, new SamlSerializer(), new WSSecurityTokenSerializer());

                memWriter.Flush();

                ms.Seek(0, SeekOrigin.Begin);

 

                txtAssertion = new StreamReader(ms).ReadToEnd();

            }

            return txtAssertion;

        }

    }

 

 

 

    class EncryptedKeyResolver : SecurityTokenResolver

    {

        // Relying party certificate – must hold private key.

        X509Certificate2 rpCert;

        public EncryptedKeyResolver(X509Certificate2 rpCert)

        {

            this.rpCert = rpCert;

        }

        protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)

        {

            key = null;

            try

            {

                var ekec = keyIdentifierClause as EncryptedKeyIdentifierClause;

                if (ekec != null)

                {

                    switch (ekec.EncryptionMethod)

                    {

                        case http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p&#8221;:

                            {

                                var encKey = ekec.GetEncryptedKey();

 

                                var rsa = rpCert.PrivateKey as RSACryptoServiceProvider;

                                var decKey = rsa.Decrypt(encKey, true);

 

                                key = new InMemorySymmetricSecurityKey(decKey);

                                return true;

                            }

                    }

                    var data = ekec.GetEncryptedKey();

                    var id = ekec.EncryptingKeyIdentifier;

                }

            }

            catch (Exception eexx)

            {

                Console.WriteLine(eexx);

                throw eexx;

            }

            return false;

        }

 

        protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)

        {

            throw new NotImplementedException();

        }

 

        protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)

        {

            throw new NotImplementedException();

        }

    }

Posted in Federation/STS, WIF | 7 Comments »

Pass-through SAML tokens & Secure Conversation (Part2)

Posted by zamd on June 7, 2009

Few people have contacted me regarding follow-up on my last post so this post continues where the last one left off.

To keep the API simple, I have created few extension methods. Let’s first review these.

public static class Extensions

{

    public static void EnableSamlTokenCaching(this ServiceHostBase source)

    {

        var orignal = source.Description.Behaviors.Remove<ServiceCredentials>();

        source.Description.Behaviors.Add(new ServiceCredentialsEx(orignal));

    }

 

    public static SamlSecurityToken GetIncommingSamlToken(this ServiceSecurityContext source)

    {

        var policy = source.AuthorizationPolicies.OfType<SamlTokenCachingPolicy>().FirstOrDefault();

        if (policy != null)

            return policy.IncommingSamlToken;

        return null;

    }

 

    public static void EnableSamlTokenFlow<TChannel>(this ClientBase<TChannel> source, SamlSecurityToken tokenToFlow) where TChannel : class

    {

        var orignal = source.Endpoint.Behaviors.Remove<ClientCredentials>();

        source.Endpoint.Behaviors.Add(new ClientCredentialsWrapper(orignal));

    }

}

EnableSamlTokenCaching is what you have to call to enable SAML token caching functionality for a particular ServiceHost. Here is an example of a simple host (created using a custom factory (IIS/WAS hosted scenario)) being configured with this API

public class EchoServiceHostFactory : ServiceHostFactoryBase

{

    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)

    {

        var sh = new ServiceHost(typeof(EchoService), baseAddresses);

        sh.EnableSamlTokenCaching();

        return sh;

    }

}

The highlighted call simply injects couple of customized components (interceptors) into WCF’s security framework. Specifically it is injecting a new customized SamlSecurityTokenAuthenticator, which wraps the original authenticator and simply delegates all calls to it. 

SamlSecurityTokenAuthenticator is called during secure conversation handshake. Here our custom authenticator (SamlSecurityTokenAuthenticatorEx) enables us to get hold of the incoming SAML token etc.

public class ServiceCredentialsEx : ServiceCredentials

{

    public ServiceCredentialsEx(ServiceCredentials other)

        : base(other){}

 

    public override SecurityTokenManager CreateSecurityTokenManager()

    {

        return new ServiceCredentialsSecurityTokenManagerEx(this);

    }

    protected override ServiceCredentials CloneCore()

    {

        return new ServiceCredentialsEx(this);

    }

}

 

public class ServiceCredentialsSecurityTokenManagerEx : ServiceCredentialsSecurityTokenManager

{

    public ServiceCredentialsSecurityTokenManagerEx(ServiceCredentials parent)

        : base(parent){}

 

    public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)

    {

        var authenticator = base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);

        if (authenticator is SamlSecurityTokenAuthenticator)

            return new SamlSecurityTokenAuthenticatorEx((SamlSecurityTokenAuthenticator)authenticator);

        return authenticator;

    }

}

Inside SamlSecurityTokenAuthenticatorEx, I’m simply creating an additional token caching policy and   adding it to the collection of policies created by default SAML token authenticator.

public class SamlSecurityTokenAuthenticatorEx : SamlSecurityTokenAuthenticator

{

    SamlSecurityTokenAuthenticator wrapped;

 

    public SamlSecurityTokenAuthenticatorEx(SamlSecurityTokenAuthenticator wrapped)

        : base(new List<SecurityTokenAuthenticator>())

    {

        this.wrapped = wrapped;

    }

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)

    {

        var orignalPolicies = wrapped.ValidateToken(token);

        List<IAuthorizationPolicy> finalPolicies = new List<IAuthorizationPolicy>(orignalPolicies);

 

        finalPolicies.Add(new SamlTokenCachingPolicy((SamlSecurityToken)token));

        return finalPolicies.AsReadOnly();

    }

}

My SamlTokenCachingPolicy is just a wrapped around the cached token.

public class SamlTokenCachingPolicy : IAuthorizationPolicy

{

    public const string PolicyID = “{32835E28-3ED4-42d4-A2EA-FA71E13AF51F}”;

    public SamlTokenCachingPolicy(SamlSecurityToken token)

    {

        this.IncommingSamlToken = token;

    }

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)

    {

        return true;

    }

 

    public ClaimSet Issuer

    {

        get { throw new System.NotImplementedException(); }

    }

 

    public string Id

    {

        get { return PolicyID; }

    }

 

    public SamlSecurityToken IncommingSamlToken { get; private set; }

}

IAuthorizationPolicy model nicely aligns with WCF security framework (so I have used it here). If you are not comfortable with this approach, you could get the same results by choosing a different caching strategy.

Inside your service method you can get hold of the incoming SAML token using following piece of code.

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Prefix)]

public class EchoService : IEchoService

{

    public string Echo(string input)

    {

        // Get token from incoming security context..

        var samlToken = OperationContext.Current.ServiceSecurityContext.GetIncommingSamlToken();

    }

}

GetIncommingSamlToken is again an extension which extracts the actual token from my custom AuthorizationPolicy(SamlTokenCachingPolicy).

    public static SamlSecurityToken GetIncommingSamlToken(this ServiceSecurityContext source)

    {

        var policy = source.AuthorizationPolicies.OfType<SamlTokenCachingPolicy>().FirstOrDefault();

        if (policy != null)

            return policy.IncommingSamlToken; 

        return null;

    }

Oh, this post is already way too long. Probably next time I will show you, how to pass this token to backend services (sharing the same certificate).

Posted in Federation/STS | 3 Comments »

Pass-through SAML tokens & Secure Conversation (Part1)

Posted by zamd on May 8, 2009

Part 2

Let’s say you have implemented federated security and now you have bunch of services all of them requires a token from a trusted STS to provide any service. Now some of these services are logically part of same security realm but are still distinct entities.  To optimize performance, you probably want to do some short circuiting so that when a user presents a token (issued by trusted STS) to one of these services – then this frontend service should simply be able to forward this incoming token to backend services within its logical security boundary (akin to trusted delegation). Here is a diagrammatic view.

On the surface, this sounds like a simple scenario which can be implemented as:

·         Reach into incoming service security context

·         Extract the incoming token

·         Pass it on to backend service

The trouble here is: wsFederationHttpBinding wisely uses secure conversation, to avoid token acquisition overhead for each call. Due to this optimization, we never get to see the actual bootstrap token (token used to establish secure conversation session) inside our service methods.

 In the next post, I will show you how to extend WCF security framework to enable this scenario?

At a very high level, we need to hook into secure conversation handshake, extract the incoming SAML token and save it somewhere for future use.

 

Posted in Federation/STS, WCF | Leave a Comment »

Forms Auth & Federated Security (part 2)

Posted by zamd on April 25, 2009

Here I talked about an approach that you can use to integrate your legacyJ  forms auth applications with STS and bring them into the world of federated security.    

One major hurdle in implementing this approach is to flow Forms Auth cookie to the STS so that it can authenticate the caller and can issue a token. With wsFederationHttpBinding, you don’t directly talk to the STS rather federation binding talks to the STS as part of your service call.  After wsFederationHttpBinding successfully acquired a token, only then your service is called and token is sent as part of the call. This is good because it hides all the token acquisition/forwarding complexity from you and offers you a simple programming model. 

Now in our case, we need to intercept the messages sent by FedBinding to the STS so that we can send our Forms Auth cookie along with the message. 

At this point a very brief diagrammatic overview of WCF message security framework will help: 

 

On the client side TokenProvider is responsible for providing tokens to message security layer. There is TokenProvider for each type of type (Usernname, IssuedToken etc). 

IssuedSecurityTokenProvider is used when a SAML token is required by the message security layer and this is the guy we need to intercept. 

Let’s start by creating a custom ClientCredentials: 

public class ClientCredentialsWrapper : ClientCredentials 

{ 

     public ClientCredentialsWrapper() 

    {} 

    public ClientCredentialsWrapper(ClientCredentials other):base(other) 

    {} 

    public override SecurityTokenManager CreateSecurityTokenManager() 

    { 

        return new ClientSecurityTokenManagerWrapper(this); 

    } 

    protected override ClientCredentials CloneCore() 

    { 

        return new ClientCredentialsWrapper(this); 

    } 

} 

Next our custom SecurityTokenManager: 

class ClientSecurityTokenManagerWrapper : ClientCredentialsSecurityTokenManager 

{ 

    public ClientSecurityTokenManagerWrapper(ClientCredentials parent) 

        : base(parent) 

    { } 

    public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement) 

    { 

        var provider = base.CreateSecurityTokenProvider(tokenRequirement); 

        var issuedProvider = provider as IssuedSecurityTokenProvider; 

        if (issuedProvider != null) 

            issuedProvider.IssuerChannelBehaviors.Add(new MessageInspectorInstallerBehavior()); 

        return provider; 

    } 

} 

Here I have added endpoint behaviour in the ChannelFactory used by IssuedSecurityTokenProvider to talk to STS. 

  

 class MessageInspectorInstallerBehavior : IEndpointBehavior 

{ 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 

    { } 

  

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 

    { 

        clientRuntime.MessageInspectors.Add(new CookieFlowMessageInspector()); 

    } 

  

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 

    { } 

  

    public void Validate(ServiceEndpoint endpoint) 

    { } 

} 

Here I’m simply installing a MessageInspector which looks like this: 

class CookieFlowMessageInspector : IClientMessageInspector 

{ 

    public void AfterReceiveReply(ref Message reply, object correlationState) 

    { } 

  

    public object BeforeSendRequest(ref Message request, IClientChannel channel) 

    { 

        object prop = null; 

        HttpRequestMessageProperty rmp = null; 

        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out prop)) 

            rmp = prop as HttpRequestMessageProperty; 

        else 

            rmp = new HttpRequestMessageProperty(); 

  

        rmp.Headers[HttpRequestHeader.Cookie] = FormAuthUtility.GetCookieHeaderFromRequest(); 

        request.Properties[HttpRequestMessageProperty.Name] = rmp; 

        return null; 

    } 

} 

Client code will stay the same and you just have to call just one extension method to hook everything together. 

 var proxy = new STSCookieServiceReference.EchoServiceClient(); 

var certPath = HttpContext.Current.Server.MapPath(“~/localhost.cer”); 

proxy.ClientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(certPath); 

  

proxy.EnableIssuedTokenProviderCookieFlow();  

proxy.Echo(“Welcome.”); 

And this is how extension method is implemented. 

public static void EnableIssuedTokenProviderCookieFlow<TChannel>(this ClientBase<TChannel> source) where TChannel : class 

{     

    var orignal = source.Endpoint.Behaviors.Remove<ClientCredentials>(); 

    source.Endpoint.Behaviors.Add(new ClientCredentialsWrapper(orignal)); 

} 

I have attached complete solution with this post. Feel free to download and have a look.  

  

  

Download: FormsAuthFedSecurity.zip
 

Posted in Federation/STS, WCF | 3 Comments »

Integrating Forms Authentication and Federated Security

Posted by zamd on April 15, 2009

Here I have talked about a technique to flow Forms Authentication cookie to a WCF service and using it as an authentication credential for the service.  For this to work – service must be running in ASP.net compatibility mode and Forms Authentication must be configured in web.config.

Here is diagrammatic view of a simple case.

In the above diagram, website authenticates users using Forms authentication, which creates a cookie after successful authentication. When ASP.net sites calls the backend service (in response to users action) – it simply passes the Form Auth cookie as part of the call. Now because our service is running in asp.net compatibility mode and Forms Authentication module is also configured in web.config, cookie will successfully be validated and we will see correct identity in the HttpContext.User

Looking good so far. Yes?

I think, this is acceptable when you only have one to two services – however when we talk about SOA – we generally mean larger number of services and this is where this approach becomes clunky primarily due to following two reasons:

·         Every service MUST run under asp.net compatibility mode, which is slower than the default native mode. So you incur some performance plenty.

·         For the cookie flow to work, both participants MUST use the same <machineKey>.  As your share single key among more and more participants – the overall security of solution is getting weaker as key will be duplicated across many places.

So there are some scalability & security issues in the above architecture.

 Let’s see how we can improve this architecture by introducing an STS into picture. The goal here is to extract the authentication mechanism (FormsAuth cookie based) out of the services and move it to STS. Service will simply require a token from STS – it is STS’s responsibility to authenticate user using some mechanism (Forms Auth being the step one).

Here you can see ONLY STS is running in asp.net compatibility mode and services are running under native mode which will result in better performance.

Also now we are only sharing keys (<machineKey>) between two participants (STS  & Website) regardless of the number of services. Services are simply configured to require a SAML token from the STS and are agnostic to the actual client authentication mechanism. We can easily change cookie based authentication with Kerberos without impacting services at all.

Now Step 1 isn’t really possible out of box – why?

Because it’s the wsFederationHttpBinding, which talks to STS rather your proxy (configured to use wsFederationHttpBinding). So setting cookie on the proxy will NOT send it to STS and the solution is to extend WCF security framework to enable cookie flow to the STS.

In next post, I will show how to implement Step 1 while still leveraging wsFederationHttpBinding. For now please see this post for background.

 

 

Posted in Federation/STS, WCF | 1 Comment »

Geneva Framework & S4U

Posted by zamd on December 23, 2008

Current bits of Geneva framework shipped with a service known as Microsoft "Geneva" Claims To NT Token Service. This service can be used to get a Kerberos token for a user without requiring its password. Windows Server 2003 added a little known extension to Kerberos known as Service-4-User (S4U) and this service internally uses this feature to get a Kerberos token.

 

S4U tokens usually have some special restriction to avoid their misuse. So if service is not running under LocalSystem account then the returned token will only have  impersonation level of Identify, so you can query the returned token for group information etc but you cannot impersonate it to open kernel objects etc.  However if the caller is running as LocalSystem then LSA returns a token with the impersonation level of Impersonate and you can indeed impersonate the user and access kernel object on his behalf.  “Claims To NT Token Service” runs under LocalSystem – so a token acquired using this service can be used to impersonate as well.

This service is actually exposed using a WCF endpoint and as part of Geneva framework you get a proxy client for this service as well. Here is an example of using this proxy client to get a token.

 

    class Program

    {

        static void Main(string[] args)

        {

            string filePath = @"C:\temp\data.txt";

            string data;

            var wi = S4UClient.UpnLogon("abu@bccoss.com");

            using (var wic = wi.Impersonate())

            {

                data = File.ReadAllText(filePath);

            }

        }

    }

 

Posted in Geneva | Leave a Comment »

Sharing STS object between Active & Passive clients

Posted by zamd on August 16, 2008

Geneva Framework (aka Zermatt) has clean separation between token issuance object model (SecurityTokenService, RequestSecurityTokenRequest etc) and the protocol used to request token. By exploiting this separation, I will show you how to share a single SecurityTokenService object between active (WS-Trust) and passive (WS-Federation) clients. I have already written about Geneva and active clients here so in this post I will focus on passive clients. 

Geneva Framework comes with a sample which implements WS-Federation protocol handler in an ASPX page and for most of the scenarios you will use the same approach. Please refer to “Federation Scenario For Passive Clients” sample for details. The approach I am highlighting here is specific to those scenarios where you want to share the STS object between active & passive clients (Or you don’t want to take a dependency on asp.net pipeline). To achieve this I will create a WCF based protocol handler (using the WCF http support) for WS-Federation protocol. In this handler I will convert the WS-Federation token issuance request into Zermatt token issuance object model (which is very much similar to WS-Trust protocol) and will then forward the request to a shared STS object. 

The service contract for my WS-Federation protocol handler looks like following: 

[ServiceContract] 

public interface IWSFederationService 

{ 

    [OperationContract] 

    [WebGet] 

    Message Issue(string wa, string wtrealm, string wreply, string wctx, string wct); 

} 

I have deliberately named the parameters of this method based on the elements of WS-Federation protocols. This enables WCF QueryStringFormatter to correctly map the values from the HTTP Get “query string” on to these parameters. 

public class ProcessService : IWSFederationService 

{ 

    public Message Issue(string wa, string wtrealm, string wreply, string wctx, string wct) 

    { 

        var col = OperationContext.Current.IncomingMessageProperties[“UriTemplateMatchResults”] as UriTemplateMatch; 

        // Get the shared STS object. 

        var sts = SingletonSTS.Instance; 

         

        var fedSerializer = new WSFederationSerializer(); 

        //create a WS-Federation message for the input data. 

        SignInRequestMessage fedRequest = (SignInRequestMessage)WSFederationMessage.CreateFromUri(col.RequestUri); 

        //convert the federation request into the common WS-Trust based STS model. 

        var rst = fedSerializer.CreateRequest(fedRequest, new WSTrustSerializationContext()); 

        var rstr = sts.Issue(ClaimsPrincipal.Current,rst); 

         

        var rstrStr = fedSerializer.GetResponseAsString(rstr, new WSTrustSerializationContext()); 

        //serialize the token response back into WS-Federation message. 

        var fedResponse = new SignInResponseMessage(new Uri(wreply), rstrStr); 

        fedResponse.Write(Console.Out); 

        // Set appropriate content-type etc. 

        var httpMsg =  Message.CreateMessage(MessageVersion.None, “”,new BinaryBodyWriter(fedResponse)); 

        httpMsg.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw)); 

        HttpResponseMessageProperty rmp = new HttpResponseMessageProperty(); 

        rmp.Headers[HttpResponseHeader.ContentType] = “text/html”; 

        httpMsg.Properties.Add(HttpResponseMessageProperty.Name, rmp); 

  

        return httpMsg; 

    } 

} 

With that I can host this service as any other WCF service and I’m ready to issue tokens based on WS-Federation protocol. 

class Program 

{ 

    static CustomSecurityTokenService sts; 

    static void Main(string[] args) 

    { 

        string stsAddress = http://localhost:9000/STS&#8221;; 

        SingletonSTS.Init(stsAddress); 

        // For active clients, simply expose this sts over a WS-Trust endpoint. 

        WSTrustServiceHost wsTrustSTSHost = new WSTrustServiceHost(new WSTrustServiceContract(SingletonSTS.Instance), new Uri(stsAddress)); 

        wsTrustSTSHost.AddServiceEndpoint(typeof(IWSTrustFeb2005SyncContract), 

           new WSHttpBinding(), new Uri(stsAddress)); 

        // Make the Active STS ready. 

        wsTrustSTSHost.Open(); 

        Console.WriteLine(“STS is now ready …”); 

        foreach(var ep in wsTrustSTSHost.Description.Endpoints) 

            Console.WriteLine(ep.Address.Uri.AbsoluteUri); 

  

        //make passive STS ready. 

        var wsFedSTSHost = new WebServiceHost(typeof(ProcessService), new Uri(http://localhost/wcf/federation&#8221;)); 

        wsFedSTSHost.AddServiceEndpoint(typeof(IWSFederationService), new WebHttpBinding(), “”); 

        wsFedSTSHost.Open(); 

        Console.ReadLine(); 

  

        wsFedSTSHost.Close(); 

        wsTrustSTSHost.Close(); 

    } 

}

Posted in Zermatt | 2 Comments »

SAML Token Requestor

Posted by zamd on July 11, 2008

In yesterdays post I have shown you how to create a basic STS using Zermatt. You can use this STS by configuring your clients to use WSFederationHttpBinding however in that case you will never see the token issued by STS. In this post I will show you a direct way to communicate with your STS by using the IssuedSecurityTokenProvider – This is useful for quick testing scenarios or when you need access to the issued token.

TokenProviders are the WCF components which provide the tokens used in message security. There is usually a TokenProvider for each type of security token (Certficate, UserName, Kerberos, IssuedToken etc). IssuedSecurityTokenProvider internally uses a ChannelFactory to communicate with the STS to get the actual token. You can configure this ChannelFactory by adding your custom behaviors to the IssuerChannelBehaviors property of the IssuedSecurityTokenProvider. Here is the code sample.

 

public static void Main()

{

    //This provider internally creates a WCF proxy (ChannelFactory) and uses it to issues RST request.

    IssuedSecurityTokenProvider provider = new IssuedSecurityTokenProvider();

    provider.SecurityTokenSerializer = new WSSecurityTokenSerializer();

 

    provider.TargetAddress = new EndpointAddress(http://localhost/IService&#8221;);

    provider.IssuerAddress = new EndpointAddress(http://localhost:9000/STS&#8221;);

 

    var be = new WSFederationHttpBinding().CreateBindingElements().Find<SecurityBindingElement>();

    // use the default algo & security versions used by Federation binding.

    provider.SecurityAlgorithmSuite = be.DefaultAlgorithmSuite;

    provider.MessageSecurityVersion = be.MessageSecurityVersion;

 

    // Binding used to communicate with STS.

    provider.IssuerBinding = new WSHttpBinding();

 

    // opent the internal channelfactory.

    provider.Open();

   

    // request token by issuing a WS-Trust RST request.

    var issuedToken = provider.GetToken(TimeSpan.FromMinutes(1));

   

    // print token on the console.

    WSSecurityTokenSerializer serializer = new WSSecurityTokenSerializer(be.MessageSecurityVersion.SecurityVersion);

    var writer = XmlWriter.Create(Console.OpenStandardOutput());

    serializer.WriteToken(writer, issuedToken);

    writer.Flush();

    provider.Close();

}

 

My STS requires Kerberos token to issue a SAML token – so this provider simply uses the kerberos token of the process. If you need to send different token (UserName etc) then you can do that using the IsserChannelBehaviors propery mentioned ealier.

provider.IssuerChannelBehaviors.Add(new IssuerCredBehavior());

And custom behavior looks like this.

public class IssuerCredBehavior : IEndpointBehavior

{

    #region IEndpointBehavior Members

 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

    {

        var cc = bindingParameters.Find<ClientCredentials>();

        if (cc == null)

        {

            cc = new ClientCredentials();

            cc.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            cc.ServiceCertificate.SetDefaultCertificate(“CN=localhost”, StoreLocation.LocalMachine, StoreName.My);

           //Use this client certificate to get a SAML token.

            cc.ClientCertificate.Certificate = CertificateUtil.GetCertificate(StoreName.My, StoreLocation.CurrentUser, “cn=localhost”);

            bindingParameters.Add(cc);

        }

    }

 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)   {    }

 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)    {    }

 

    public void Validate(ServiceEndpoint endpoint)    {    }

 

    #endregion

}

Posted in Federation/STS, Geneva | 7 Comments »

Creating a Basic STS using Geneva Framework( aka Zermatt)

Posted by zamd on July 10, 2008

Zermatt (Geneva Framework) is an identity framework built on top of solid WCF foundation. It makes writing custom STSs a breeze as the amount of code you have to write is substantially low compared with, when building the same STS directly on top of WCF. In this post I will show you how to write a basic STS using Zermatt. 

This STS will be used by active clients, which mean, it will expose its token issuance functionality using the WS-Trust protocol. In a later post I will talk about passive clients and WS-Federation protocol. 

So the first step is to drive a class from Zermatt provided SecurityTokenService abstract base class. This class provides most of the plubming required implementing an STS and you have to only provide missing pieces in your derived class. 

class CustomSecurityTokenService : SecurityTokenService{} 

Next you have to override following two methods: 

    protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request) 

    { 

        Scope scope = base.GetScope(principal, request); 

        scope.EncryptingCredentials = new X509EncryptingCredentials(CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, “CN=localhost”)); 

        return scope; 

    } 

  

    public override ClaimsIdentityCollection  GetOutputSubjects(Scope scope, IClaimsPrincipal principal, RequestSecurityToken request) 

    { 

        ClaimsIdentityCollection collection = new ClaimsIdentityCollection(); 

        collection.Add( new ClaimsIdentity( new Claim( ClaimTypes.Name, “zulfiqar” ) ) ); 

        return collection; 

    } 

In the GetScope method, you get all the information about the requestor and based on that you can select credentials which will be used to encrypt the issued token to the requestor. You can do that in the GetOutputSubjects method as well in which case you don’t need to override GetScope. 

In GetOutputSubjects method you create a collection of claims which will be added to the issued token. In a production STS, this is where you will access the external system to get information about the requestor and will create claims based on that information. In this example, I’m simply returning a single Name claim. That’s it – you are done with STS part. 

Now as an STS is essentially a service, so you need to host it inside a ServiceHost and expose it’s functionality using an Endpoint (Standard WCF stuff). Zermatt also provides a custom service host, known as WSTrustServiceHost – which adds Zermatt specific stuff (configuration etc) to the standard ServiceHost class. 

    SecurityTokenServiceConfiguration config = 

        new SecurityTokenServiceConfiguration(stsAddress, new X509SigningCredentials(stsCert)); 

    stsConfig.SecurityTokenService = typeof(CustomSecurityTokenService); 

    //Create host based on our configuration. 

    stsHost = new WSTrustServiceHost(stsConfig, new Uri(stsAddress)); 

    // Add an endpoint – this can be added using standard config mechansims as well. 

    stsHost.AddServiceEndpoint(typeof(IWSTrustFeb2005SyncContract), 

       new WSHttpBinding(), new Uri(stsAddress)); 

    // open the host now.            

    stsHost.Open(); 

At this point our STS is ready to go. You might have noticed that I’m using the standard WSHttpBinding, which mean my STS will authenticate requestors using windows authenticaiton (default for WSHttpBinding) – and then I can use all that information to inlcude appropraite claims in the returned SAML token. One last important point is that Zermatt is build on top of WCF security model – so I can change the binding to require a certificate or a userName to issue a SAML token.  

 

In the next post I will show how to a write a basic test harnesses for our Security Token Service. 

 

Posted in Geneva | Leave a Comment »