Showing posts with label SAML2. Show all posts
Showing posts with label SAML2. Show all posts

Sunday, 1 June 2014

Part2: Creating a SAML 2.0 Token using WIF 4.5

In Part 1 I demonstrated how to generate a simple SAML2 token using the WIF 4.5 libraries. To finish this out I will now describe how to read a SAML2 token that was previously generated with the SamlTokenProcessor class.

Read Token

In order to read an encrypted and signed SAML2 token an instance of a SecurityTokenHandler class is required that has the correct configuration. Specifically we need an instance of an Saml2SecurityTokenHandler class. It is possible to create an instance of this class and configure it through code, an alternate approach is to use the WIF configuration classes to configure and create an instance of a Saml2SecurityTokenHandler. This is a preferred approach because it provides some level of flexibility to the SamlTokenProcessor. Below is an example of how to create the Saml2SecurityTokenHandler from the IdentityConfiguration.
IdentityConfiguration configuration = new IdentityConfiguration();

_saml2SecurityTokenHandler = configuration.SecurityTokenHandlers[typeof(Saml2SecurityToken)];

_saml2SecurityTokenHandler.Configuration.ServiceTokenResolver = 
 new X509CertificateStoreTokenResolver(StoreName.My, StoreLocation.CurrentUser);

The Saml2SecurityTokenHandler instance requires a security token resolver. This is a class that is able to resolve the signing and encrypting certificates that where originally used to create the SAML2 token. The IdentityConfiguration class expects a configuration section in the application configuration file.
<configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>

The configuration required for the example application is as follows, this configuration is a basic configuration which is used for token validation, more on this later. The configuration can be used to configure replay detection for tokens or the maximum clock skew between the token generator and the token consumer.
<system.identityModel>
 <identityConfiguration>
  <audienceUris>
   <add value="http://rp.com/" />
  </audienceUris>
  <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
   <trustedIssuers>
    <add thumbprint="‎0D274A03BDCA95015E712E2808F917C93159ED1B"/>
   </trustedIssuers>
  </issuerNameRegistry>
 </identityConfiguration>
</system.identityModel>

Note: The thumbprint element value must be set to the thumbprint of the STS identity certificate that was generated or acquired from a CA. Also be careful copy and pasting the thumbprint as it can copy with BOM data in the string.

Now that we have an instance of a Saml2SecurityTokenHandler it can be used to read a token from a stream.
public SecurityToken ReadSecurityToken(Stream stream)
{
 SecurityToken securityToken;

 using(XmlReader rdr = new XmlTextReader(stream))
 {
  securityToken = _saml2SecurityTokenHandler.ReadToken(rdr);
 }

 return securityToken;
}

Validate Token


The VerifySecurityToken method of the SamlTokenProcessor class can verify a SAML2 token against the configuration specified in the configuration file. A prerequisite to verifying the SAML2 token generated is to ensure that the signing token is present in the certificate store under TrustedPeople.

image

The VerifySecurityToken method will verify the attributes of the token against the configuration. As a test the audienceUri can be modified to cause the  verification to fail.
<audienceUris>
 <add value="http://rp2.com/" />
</audienceUris>
When the test application is run using this configuration an AudienceUriValidationFailedException exception will be thrown.
 
System.IdentityModel.Tokens.AudienceUriValidationFailedException: ID1038: The AudienceRestrictionCondition was not valid because the specified Audience is not present in AudienceUris.
Audience: 'http://rp.com/'
   at System.IdentityModel.Tokens.Saml2SecurityTokenHandler.ValidateToken(SecurityToken token)
   at CoreMemoryDump.Saml.SamlTokenProcessor.VerifySecurityToken(SecurityToken securityToken) in c:\Users\Office\Documents\Visual Studio 2012\Projects\CoreMemoryDump.Saml\CoreMemoryDump.Saml\SamlToken
Processor.cs:line 92
   at CoreMemoryDump.SamlTest.Program.Main(String[] args) in c:\Users\Office\Documents\Visual Studio 2012\Projects\CoreMemoryDump.Saml\CoreMemoryDump.SamlTest\Program.cs:line 47

Similarly modifying the TrustedIssuers list will elicit an exception indicating that the issuer is not trusted.

Summary


The intent of this example code was to show that it is possible to generate, read and verify a SAML token using the base libraries from the WIF 4.5 framework. This example provides a basic implementation that can be extended to enable greater flexibility for generating SAML tokens.  


The code is available here: GitHub DumpingCoreMemory.Saml

Thursday, 1 May 2014

Part1: Creating a SAML 2.0 Token using WIF 4.5

To create a SAML token we will need a set of certificates for the Relying Party (RP) and Secure Token Service (STS). These certificates plus keys will be used for the signing and encryption of the SAML 2.0 token. In the  part 2 of this series the keys will be used for signature verification and decryption.
First we need a certificate for the certificate authority that will be used as the issuer for the STS and Relying Party. Note you will be prompted for a password for the CA private key file, use a simple password as you will be prompted for this password when creating the certificates for the RP.
makecert -n "CN=RootCATest" -r -sv RootCATest.pvk RootCATest.cer
To create the certificate for the STS and Relying Party the following command can be executed from a batch file.
makecert -sk %1 -iv RootCATest.pvk -n CN=%1 -ic RootCATest.cer –sr localmachine -ss my -sky exchange -pe %1.cer
For example createcert.bat RP will create the certificate for the RP. Note that all of the certificate creation commands must be executed from a VS2012/2010 command prompt that is running with administrator privileges.
Later we will place the certificates in the relevant certificate stores for use later in the process of developing the SAML token generator.

Token Generation

To generate a SAML token a SecurityTokenDescriptor must be created. This is a place holder for the attributes of an issued token.
private SecurityTokenDescriptor BuildBaseSecurityTokenDescriptor(Uri appliesToAddress, String issuer, TimeSpan validityPeriod, 
            IEnumerable<Claim> claims, X509Certificate2 encryptingCertificate, X509Certificate2 signingCertificate)
{
  DateTime utcNow = DateTime.UtcNow;
  SecurityTokenDescriptor securityTokenDescriptor = new SecurityTokenDescriptor();
  securityTokenDescriptor.AppliesToAddress = appliesToAddress.ToString();
  securityTokenDescriptor.TokenIssuerName = issuer;
  securityTokenDescriptor.Lifetime = new Lifetime(utcNow, utcNow.Add(validityPeriod));
  securityTokenDescriptor.SigningCredentials = new X509SigningCredentials(signingCertificate);
  securityTokenDescriptor.Subject = new ClaimsIdentity(claims);
  securityTokenDescriptor.TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
  securityTokenDescriptor.EncryptingCredentials = new EncryptedKeyEncryptingCredentials(encryptingCertificate);

  return securityTokenDescriptor;
}

To generate the token a SecurityTokenHandlerCollection is constructed with a collection of SecurityToken handlers. To generate a SAML token we use a Saml2SecurityTokenHandler, to generate the Saml2 token and a EncryptedSecurityTokenHandler to encrypt and sign the token when the token is serialized.
public SamlTokenProcessor()
{
  _securityTokenHandlerCollection = new SecurityTokenHandlerCollection();
  _securityTokenHandlerCollection.Add(new Saml2SecurityTokenHandler());
  _securityTokenHandlerCollection.Add(new EncryptedSecurityTokenHandler());
}

Calling the CreateSecurityToken method generates a Saml2SecurityToken instance.
SamlTokenProcessor processor = new SamlTokenProcessor();

X509Certificate2 encryptingCertificate = CertificateResolver.ResolveCertificate(StoreLocation.LocalMachine, StoreName.My, "CN=RP");
X509Certificate2 signingCertificate = CertificateResolver.ResolveCertificate(StoreLocation.LocalMachine, StoreName.My, "CN=STS");

List<Claim> claims = new List<Claim>() { new Claim("Claim", "Value") };

SecurityToken token = processor.CreateSecurityToken(new Uri("http://rp.com"), "sts", new TimeSpan(0, 0, 10), claims, 
 encryptingCertificate, signingCertificate);

using(Stream stream = new FileStream("token.xml", FileMode.OpenOrCreate))
{
 processor.SerializeSecurityToken(token, stream);
}

The SerializeSecurityToken method serializes the token to a stream. This applies the signing and encryption to the resultant tokens XML representation. Below is an example token shortenedshortend for brevity.
<?xml version="1.0" encoding="utf-8"?>
<EncryptedAssertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
  <xenc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
        <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
          <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        </e:EncryptionMethod>
        <KeyInfo>
          <o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <X509Data>
              <X509IssuerSerial>
                <X509IssuerName>CN=RootCATest</X509IssuerName>
                <X509SerialNumber>78425684050690387325969986111748521388</X509SerialNumber>
              </X509IssuerSerial>
            </X509Data>
          </o:SecurityTokenReference>
        </KeyInfo>
        <e:CipherData>
          <e:CipherValue>NSmOOCR6CzGCj+lY2PF....</e:CipherValue>
        </e:CipherData>
      </e:EncryptedKey>
    </KeyInfo>
    <xenc:CipherData>
      <xenc:CipherValue>e5kGcfcel/YN+Qjz....</xenc:CipherValue>
    </xenc:CipherData>
  </xenc:EncryptedData>
</EncryptedAssertion>

In the second part of this two part blog I will demonstrate how to reverse the process and extract the claims from the encrypted SAML2 token.

The code is available here: GitHub DumpingCoreMemory.Saml

Tuesday, 1 April 2014

Creating and Consuming SAML 2.0 Tokens using WIF

With the introduction of Windows Identity Foundation 4.5 Microsoft removed the Visual Studio templates that could be used to generate a custom WCF STS service. The custom STS could then be used to issue SAML 2.0 tokens based on custom credentials passed with the WS-Trust request to the custom STS.

The underlying classes are still available to use for creating a custom STS, without the templates available it is still possible to create a custom WCF STS service but it requires a bit more effort.

There is however an alternate means to generate SAML 2.0 tokens using WIF that forgoes the need to create a custom STS. This is useful in scenarios involving proprietary systems that have custom protocols and authentication, that also do not lend themselves well to integration with ADFS etc.

To explain how this can be achieved I will break the problem down into two parts:

Part 1: Creating a SAML 2.0 Token using WIF 4.5

Part2: Consuming a SAML 2.0 Token using WIF 4.5