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

No comments:

Post a Comment