requesting an asymmetric proof key from ACS


Using only WIF libraries for RSTs and ChannelFactories, endorsing signatures for the binding, custom securitytoken manager from the WCF samples SAMLtoken project (as modified to create RSASecurityTokens from the private key associated with an X.509Certificate2 when so required), we have managed to create a signed RST message using our *own* RSA key pair. The message is not dissimilar to that produced by WCF’s issuedsecuritytokenprovider (when using, in contrast, an ephemeral RSA key).

image

The code is a mix of work of WCF samples and a Travis Spencer sample (modified to point at ACS, rather than Ping Identities products).

#define ws13
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using System.Text;
using Microsoft.IdentityModel.Protocols.WSTrust;
using Microsoft.IdentityModel.Protocols.WSTrust.Bindings;
using Microsoft.Samples.SamlTokenProvider;

namespace ConsoleApplication4
{
    class Program
    {
        private const string userName = "rapstaff@rapmls.info";
        private const string password = "***";
#if ws13
        private const string appliesTo = "https://www.rapmls.info:44302/api2/string.svc";
        private const string stsAddress = @"https://bariazuresso.accesscontrol.windows.net/v2/wstrust/13/username";
#else
        private const string appliesTo = "urn:federation:MicrosoftOnline";
        private const string stsAddress = @"https://www.rapmls.info/Issuer.svc/...";
#endif
        static void Main()
        {
            IgnoreCertificateValidation();
            var rstr = RequestSecurityToken();
            PrintRequestSecurityTokenResponse(rstr);

            Console.ReadLine();
        }

        private static void IgnoreCertificateValidation()
        {
            ServicePointManager.ServerCertificateValidationCallback = (sender,cert, chain, errors) => true;
        }

        private static void PrintRequestSecurityTokenResponse(RequestSecurityTokenResponse rstr)
        {
            Console.WriteLine("Security token issued by ACS:");
            Console.WriteLine(rstr.RequestedSecurityToken.SecurityTokenXml.InnerXml);
        }

        private static RequestSecurityTokenResponse RequestSecurityToken()
        {
            var factory = GetChannelFactory();

            RsaSecurityToken rsat = new RsaSecurityToken(factory.Credentials.ClientCertificate.Certificate.PublicKey.Key as RSA);

#if ws13
            var rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Asymmetric)
#else
            var rst = new RequestSecurityToken(WSTrustFeb2005Constants.RequestTypes.Issue, WSTrustFeb2005Constants.KeyTypes.Asymmetric)
#endif
            {
                AppliesTo = new EndpointAddress(appliesTo),
                UseKey = new UseKey(new SecurityKeyIdentifier(new RsaKeyIdentifierClause(rsat.Rsa)), new RsaSecurityToken(rsat.Rsa))
            };

            // Create supporting token parameters for the client X.509 certificate's RSA key.
            RsaSecurityTokenParameters clientSupportingTokenParameters = new RsaSecurityTokenParameters();
            clientSupportingTokenParameters.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
            clientSupportingTokenParameters.RequireDerivedKeys = false;

            SecurityBindingElement be = factory.Endpoint.Binding.CreateBindingElements()[0] as SecurityBindingElement;
            be.EndpointSupportingTokenParameters.Endorsing.Add(clientSupportingTokenParameters);

            var channel = factory.CreateChannel() as WSTrustChannel;

            RequestSecurityTokenResponse rstr;
            var token = channel.Issue(rst, out rstr);
            return rstr;
        }

        private static WSTrustChannelFactory GetChannelFactory()
        {
            var binding = GetBinding();
            var endpoint = new EndpointAddress(stsAddress);

            var factory = new WSTrustChannelFactory(binding, endpoint)
            {
#if ws13
                TrustVersion = TrustVersion.WSTrust13
#else
                TrustVersion = TrustVersion.WSTrustFeb2005
#endif            };

            SamlClientCredentials samlCC = new SamlClientCredentials();

            // Set the client certificate. This is the cert that will be used to sign the SAML token in the symmetric proof key case
            samlCC.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "localhost");
            samlCC.ProofToken = new RsaSecurityToken(samlCC.ClientCertificate.Certificate.PrivateKey as RSA);

            samlCC.UserName.UserName = userName;
            samlCC.UserName.Password = password;

            factory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
            factory.Endpoint.Behaviors.Add(samlCC);

            return factory;
        }

        private static Binding GetBinding()
        {
            
            SecurityBindingElement x = SecurityBindingElement.CreateUserNameOverTransportBindingElement();

            // Create supporting token parameters for the client X.509 certificate's RSA key.
            RsaSecurityTokenParameters clientSupportingTokenParameters = new RsaSecurityTokenParameters();
            clientSupportingTokenParameters.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
            clientSupportingTokenParameters.RequireDerivedKeys = false;

            x.EndpointSupportingTokenParameters.Endorsing.Add(clientSupportingTokenParameters);

            var binding = new CustomBinding();
            binding.Elements.AddRange(new BindingElement[]
            {
                x,
#if ws13
                new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, Encoding.UTF8),
#else
                new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressingAugust2004, Encoding.UTF8),
#endif
                new HttpsTransportBindingElement(),
            });

            return binding;
        }
    }
}

WCF sample samltokenprovider (and associated tokenmanager, clientcredentials) class, modified to support RSASecurityTokens

//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

using System.IdentityModel.Claims;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;

using System.ServiceModel.Description;

namespace Microsoft.Samples.SamlTokenProvider
{
    /// <summary>
    /// Custom client credentials class that allows a SAML assertion and associated proof token to be specified
    /// </summary>
    public class SamlClientCredentials : ClientCredentials
    {

        /// <summary>
        /// The proof token associated with the SAML assertion
        /// </summary>
        SecurityToken proofToken;
        
        /// <summary>
        /// Default constructor
        /// </summary>
        public SamlClientCredentials()
            : base()
        {
            // Set SupportInteractive to false to suppress Cardspace UI
            base.SupportInteractive = false;
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="other">The SamlClientCredentials to create a copy of</param>
        protected SamlClientCredentials(SamlClientCredentials other) : base ( other )
        {
            // Just do reference copy given sample nature
            this.proofToken = other.proofToken;            
        }

        /// <summary>
        /// Property allowing the proof token to be specified and retrieved
        /// </summary>
        public SecurityToken ProofToken { get { return proofToken; } set { proofToken = value; } }


        protected override ClientCredentials CloneCore()
        {
            return new SamlClientCredentials(this);            
        }

        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            // return custom security token manager
            return new SamlSecurityTokenManager(this);
        }
    }
}
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;

using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;

namespace Microsoft.Samples.SamlTokenProvider
{
    /// <summary>
    /// Custom SecurityTokenManager that creates a custom SecurityTokenProvider
    /// </summary>
    public class SamlSecurityTokenManager : ClientCredentialsSecurityTokenManager
    {
        /// <summary>
        /// The SamlClientCredentials that created us
        /// </summary>
        SamlClientCredentials samlClientCredentials;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="samlClientCredentials">The creating SamlClientCredentials instance</param>
        public SamlSecurityTokenManager(SamlClientCredentials samlClientCredentials)
            : base(samlClientCredentials)
        {
            // Store the creating client credentials
            this.samlClientCredentials = samlClientCredentials;
        }

        /// <summary>
        /// Creates the custom SecurityTokenProvider when SAML tokens are specified in the tokenRequirement
        /// </summary>
        /// <param name="tokenRequirement">A SecurityTokenRequirement  </param>
        /// <returns>The appropriate SecurityTokenProvider</returns>
        public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
        {
            // If token requirement matches SAML token return the custom SAML token provider            
            if (tokenRequirement.TokenType == SecurityTokenTypes.Rsa)
            {
                SecurityToken prooftoken = this.samlClientCredentials.ProofToken;

                return new SamlSecurityTokenProvider(prooftoken);
            }
            // otherwise use base implementation
            else
            {
                return base.CreateSecurityTokenProvider(tokenRequirement);
            }                
        }
    }
}
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.IO;
using System.ServiceModel.Security;
using System.Xml;

namespace Microsoft.Samples.SamlTokenProvider
{
    /// <summary>
    /// class that derives from SecurityTokenProvider and returns a SecurityToken that represents an RSA key.
    /// </summary>
    public class SamlSecurityTokenProvider : SecurityTokenProvider
    {

        /// <summary>
        /// The proof token associated with the SAML assertion.
        /// </summary>
        SecurityToken proofToken;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="proofToken">The proof token associated with the SAML assertion</param>
        public SamlSecurityTokenProvider(SecurityToken proofToken )
        {
            this.proofToken = proofToken;
        }

        /// <summary>
        /// Creates the security token
        /// </summary>
        /// <param name="timeout">Maximum amount of time the method is supposed to take. Ignored in this implementation.</param>
        /// <returns>A SecurityToken that corresponds to the SAML assertion and proof key specified at construction time</returns>
        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            return proofToken;
        }
    }
}

Advertisements

About home_pw@msn.com

Computer Programmer who often does network administration with focus on security servers. Very strong in Microsoft Azure cloud!
This entry was posted in SSO. Bookmark the permalink.