Decrypting SAML 2 assertion using .NET 4.5 (System.IdentityModel) / WIF

Vidar Kongsli picture Vidar Kongsli · Feb 11, 2015 · Viewed 8.4k times · Source

I am trying to decrypt an encrypted SAML 2.0 assertion issued from a Java-based Identity Provider.

Given the following setup of security token handlers:

X509Certificate2 cert = ... // Contains private key
var serviceTokens = new List<SecurityToken>();
serviceTokens.Add(new X509SecurityToken(cert));

var issuers = new ConfigurationBasedIssuerNameRegistry(); 
issuers.AddTrustedIssuer("...thumbprint...", "nottherealname");

var configuration = new SecurityTokenHandlerConfiguration
            {
                AudienceRestriction = { AudienceMode = AudienceUriMode.Never },
                CertificateValidationMode = X509CertificateValidationMode.None,
                RevocationMode = X509RevocationMode.NoCheck,
                IssuerNameRegistry = issuers,
                MaxClockSkew = TimeSpan.FromMinutes(5),
                ServiceTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(serviceTokens.AsReadOnly(), false)
            };

var tokenHandlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection(configuration);

I get an encrypted SAML assertion such as this:

<saml:EncryptedAssertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
  <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <xenc:EncryptedKey>
        <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
        <xenc:CipherData>


    <xenc:CipherValue>Fwhv/zEVi3eQvQN372L1S+pVDM5JKs1Kc2I25djuiOPdwKReCXRhnd5QL4Y8wJDWZ5vAlOxHkNAZ
OwOg4NsSI8KssrygNk4fwvNdVAGMB5ytI1QTGRqG6WwP4Em+uLN3VXbqiLWA9D6uO0BwATF9HdTb
j/IMhGCxZ1ZKrKQF5OL2PHKf4DqyNa5d9CNZenhYyYghgYrhgZtQVl/VARAp9VKsM/lbkPsEU8Ty
ow4LnTlYqBnykrOEJowN5B+HXGvfhbIBHyGzdCC+WbcEbI898zy/VhZ63VyFL2GSTdDWv10IEMy5
CHom4Qruer1xpyQMrxJ6EK30HMhVppToivgoFQ==</xenc:CipherValue>
        </xenc:CipherData>
      </xenc:EncryptedKey>
    </ds:KeyInfo>
    <xenc:CipherData>
      <xenc:CipherValue>...</xenc:CipherValue>
    </xenc:CipherData>
  </xenc:EncryptedData>
</saml:EncryptedAssertion>

When I try to read the token:

var tokenReader = new XmlNodeReader(xmlDoc); // XML document with root element <saml:EncryptedAssertion ....
if (!tokenHandlers.CanReadToken(tokenReader)) throw new Exception("Unreadable token");

var token = tokenHandlers.ReadToken(tokenReader);

I then get the following exception on the last code line:

 ID4022: The key needed to decrypt the encrypted security token could not be resolved. Ensure that the SecurityTokenResolver is populated with the required key.

According the identity provider, the symmetric key used to encrypt the payload is encrypted with my public key. Still, it seems to be unable to use the private key in the X509 certificate to decrypt the key. This is my interpretation of the error message. Could it be that the error message is wrong? What else could be wrong? Is my configuration incomplete?

Answer

d89761 picture d89761 · Feb 14, 2015

I had this problem, too, and I ended up getting past it with something like the answer to this question: How to disable Subject Key Identifier in SecurityTokenResolver

Change your existing line

ServiceTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(serviceTokens.AsReadOnly(), false)

to

ServiceTokenResolver = new Saml2SSOSecurityTokenResolver(serviceTokens)

and add a class like this:

private class Saml2SSOSecurityTokenResolver : SecurityTokenResolver
{
    List<SecurityToken> _tokens;

    public Saml2SSOSecurityTokenResolver(List<SecurityToken> tokens)
    {
        _tokens = tokens;
    }
    protected override bool TryResolveSecurityKeyCore(System.IdentityModel.Tokens.SecurityKeyIdentifierClause keyIdentifierClause, out System.IdentityModel.Tokens.SecurityKey key)
    {
        var token = _tokens[0] as X509SecurityToken;

        var myCert = token.Certificate;

        key = null;

        var ekec = keyIdentifierClause as EncryptedKeyIdentifierClause;

        if (ekec != null)
        {
            if (ekec.EncryptionMethod == "http://www.w3.org/2001/04/xmlenc#rsa-1_5")
            {
                var encKey = ekec.GetEncryptedKey();
                var rsa = myCert.PrivateKey as RSACryptoServiceProvider;
                var decKey = rsa.Decrypt(encKey, false);
                key = new InMemorySymmetricSecurityKey(decKey);
                return true;
            }

            var data = ekec.GetEncryptedKey();
            var id = ekec.EncryptingKeyIdentifier;
        }

        return true;
    }

    protected override bool TryResolveTokenCore(System.IdentityModel.Tokens.SecurityKeyIdentifierClause keyIdentifierClause, out System.IdentityModel.Tokens.SecurityToken token)
    {
        throw new NotImplementedException();
    }

    protected override bool TryResolveTokenCore(System.IdentityModel.Tokens.SecurityKeyIdentifier keyIdentifier, out System.IdentityModel.Tokens.SecurityToken token)
    {
        throw new NotImplementedException();
    }
}