Zulfiqar's weblog

Architecture, security & random .Net

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”);

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

            fac.Open();

           

            var rst = new RequestSecurityToken {

                AppliesTo = new EndpointAddress(http://localhost”),

                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();

        }

    }

Advertisements

7 Responses to “ExtractSAMLAssertion”

  1. Sid said

    Hi Zulfiqar, Nice post.
    I have a question – does your local STS sign the token anyhow? Or is it unsigned?

    I am trying your sample code on a token issued by Live IdP and I am getting the following exception:

    Microsoft.IdentityModel.Protocols.XmlSignature.SignatureVerificationFailedException was unhandled
    Message=ID4037: The key needed to verify the signature could not be resolved from the following security key identifier ‘SecurityKeyIdentifier
    (
    IsReadOnly = False,
    Count = 1,
    Clause[0] = X509SubjectKeyIdentifierClause(SKI = 0x7FA27C60886C4333E450D30EA1D58C5329A80341)
    )
    ‘. Ensure that the SecurityTokenResolver is populated with the required key.

    Cheers,
    -Sid

    • zamd said

      Hi, Yes local STS signs AND encrypt the token.
      Not sure why are you getting signature validation exception. Signature validation is usually done as part of SAML token validation and my code doesn’t do that.
      It simply returns the assertion as a string.

      • Sid said

        Hmm, not sure myself why signature validation is being done here.
        But the SKI in the exception error message above (0x7FA27C60886C4333E450D30EA1D58C5329A80341) is the SKI of Live’s certificate – I got Live’s public cert from its Fed-metadata and verified this.

        Do you know how I would set the SecurityToken derived from the issuer cert onto the SecurityTokenResolver?

      • zamd said

        Which source code are you using? Which Live IdP endpoint are you using?

  2. CRudolphi said

    Hi Zulfiqar,
    Thanks for posting very useful information.
    I’m using WIF with ACS2.0. I’ve tried your approach but found that I had to pass the InnerXml instead of the OuterXml in this line of code:
    var sr = new StringReader(token.TokenXml.OuterXml);
    when using the OuterXML, the TokenHandler complained about the XML not being positioned on an EncryptedData element. Otherwise, works great!

    One question for you: If one wanted to support that the STS might have used one of several certificates to encrypt the token (say because of certificate expiration you wanted to allow for some operational grace period in which it might be possible that the STS would use either an older or a newer certificate); would it become the responsibility of the EncryptedKeyResolver to identify which certificate was used (from the KeyIdentifierClause)? Or would one simply add multiple EncryptedSecurityTokenHandlers to the token handling collection?

  3. Hi..i am stuck very badly at one point where i need to do Ms Dynamics Crm WebLogin from my client application which is a Winform application developed in C# 4.0.My Scenario is like user will do login to CRM from desktop application and i want to show user,activity record web page of CRM.My application is able to do crm login for the user,but i am not able to show activity,user web page as i am not logged in the Web.
    Also i tries to decrypt Saml Assertion value which i got when user logged in,but dont no how to decrypt the Saml.Saml i am getting is Saml 2.0.My Ms Crm Account is of Office 365.Can you Please suggest some way out to do CRM Weblogin from my application.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: