Encrypt your WIF claims

WIF claims are per definition safe from tampering, as they are signed, and you do use SSL, don’t you? However, there might be times when you don’t want even the end user to be able to read the contents of your claims. This might be because you use claims to transport some semi-secret information, some top-secret information, or information you just don’t want the user to know you use as a means of controlling his access to content.

The information on how to encrypt your claims is available, however, the information is a bit scattered. Some information may be found here:

It was putting it all together that was the challenge for me, anyway. I have rolled my own STS, and following the code you get if you select “Add STS reference” in Visual Studio, and then “Create a new STS project in the current solution”, we set the encryption like this:

public class MySecurityTokenService : SecurityTokenService
{
    public MySecurityTokenService(SecurityTokenServiceConfiguration configuration)
        : base(configuration)
    {
    }
   
    protected override Scope GetScope( IClaimsPrincipal principal, RequestSecurityToken request )
    {
        ValidateAppliesTo( request.AppliesTo );

        Scope scope = new Scope( request.AppliesTo.Uri.OriginalString, SecurityTokenServiceConfiguration.SigningCredentials );

        string encryptingCertificateName = WebConfigurationManager.AppSettings[ "EncryptingCertificateName" ];
        if ( !string.IsNullOrEmpty( encryptingCertificateName ) )
        {
            // Important note on setting the encrypting credentials.
            // In a production deployment, you would need to select a certificate that is specific to the RP that is requesting the token.
            // You can examine the 'request' to obtain information to determine the certificate to use.
            scope.EncryptingCredentials = new X509EncryptingCredentials( CertificateUtil.GetCertificate( StoreName.My, StoreLocation.LocalMachine, encryptingCertificateName ) );
        }
        else
        {
            // If there is no encryption certificate specified, the STS will not perform encryption.
            // This will succeed for tokens that are created without keys (BearerTokens) or asymmetric keys.  
            scope.TokenEncryptionRequired = false;            
        }

        // Set the ReplyTo address for the WS-Federation passive protocol (wreply). This is the address to which responses will be directed. 
        // In this template, we have chosen to set this to the AppliesToAddress.
        scope.ReplyToAddress = scope.AppliesToAddress;

        return scope;
    }

}

The key is setting the TokenEncryptionRequired and the EncryptionCredentials on the scope.

Note also the comment in the template code, on using different certificates per RP in production code. You don’t want anyone but the RP you send the claims to decrypting the claims.
You would normally put this certificate in the “Other people” (aka “AddressBook”) certificate store, and you don’t need the private key for this certificate on the STS server(s).

Then, on the relying party (RP), you have to tell WIF where to find the certificate to use for decrypting the claims. The RP needs to have the private key of the certificate, to be able to decrypt the claims encrypted with its public key. It makes sense to put it in the “Personal” (aka “My”) certificate store.

This is done purely in configuration, in the microsoft.IdentityModel/service section of Web.config. Just insert an element like this:

 <microsoft.identityModel>
    <service>

    <!-- 

      The rest of you WIF config goes here...

    -->

  <serviceCertificate>
        <certificateReference
                    findValue="<thumbprint of the certificate used for encryption>"
                    storeLocation="LocalMachine"
                    storeName="My"
                    x509FindType="FindByThumbprint"/>
      </serviceCertificate>
    </service>
  </microsoft.identityModel>

Then you get the following in your claims set, instead of plain-text claims:

 <trust:RequestedSecurityToken>
      <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=MyCA, OU=MyOrg, O=MyComp, S=MyPlace, C=MyCountry</X509IssuerName>
                    <X509SerialNumber>***SERIAL NUMBER OF CERTIFICATE***</X509SerialNumber>
                  </X509IssuerSerial>
                </X509Data>
              </o:SecurityTokenReference>
            </KeyInfo>
            <e:CipherData>
              <e:CipherValue>***ENCRYPTED***=</e:CipherValue>
            </e:CipherData>
          </e:EncryptedKey>
        </KeyInfo>
        <xenc:CipherData>
          <xenc:CipherValue>***ENCRYPTED***</xenc:CipherValue>
        </xenc:CipherData>
      </xenc:EncryptedData>
    </trust:RequestedSecurityToken>

That should be it. Now your claims aren’t available for anyone to look at.

Advertisements
This entry was posted in .NET, C#, WIF. Bookmark the permalink.

6 Responses to Encrypt your WIF claims

  1. Thank you! Now I want to know how to decrypt it again…

    • erikbra says:

      Decryption should be handled automatically by WIF, as long as you encrypt it with a certificate where you have the private key available in the certificate store on your Relying Party.

  2. Athos Athanasiou says:

    Thanks for this blog. That’s exactly what I have. But what if what is on the other end is not WIF but Java say. How can they unencrypt the token given they have both the private and public key . Or is this encryption only for WIF to WIF?

    • erikbra says:

      I’m not sure I fully understand your scenario. Are you using WIF to login to a Java applicaion? Or some other claims-based login for a Java applicaiont?

      • Athos Athanasiou says:

        Thanks for the prompt reply Erik. I should have made myself somewhat clearer.

        We have a WIF Portal (using the architecture described above) against which we are trying to authenticate a Java Client. The java client has the public and private key . But the java client is having difficulty unencrypting the token because we are not sure exactly how WIF has encrypted it.

        Would you know the procedure WIF uses to encrypt? Or would you know if there is a way of finding out the underlying code in the WIF uses?

      • Athos Athanasiou says:

        that last bit should read
        ‘… the underlying code in the classes that WIF uses?’

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