Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 326
Vidar Kongsli Avatar asked Feb 11 '15 11:02

Vidar Kongsli


People also ask

How do I fix authentication failed on SAML?

SAML errors usually occur when there's missing or incorrect information entered during your SAML setup. You can resolve most of these issues from your IDP settings, but for some, you'll need to update your SSO settings in Slack as well.

Is SAML 2.0 encrypted?

The SAML assertions are encrypted such that the assertions can be decrypted only with the private keys held by the service provider. Note The Following: Encryption of SAML assertions is disabled by default. Responses can be signed while carrying a signed encrypted Assertion, but the Response itself is not encrypted.

Should you encrypt SAML assertion?

Encrypting the SAML assertion is optional. In most situations it isn't encrypted and privacy is provided at the transport layer using HTTPS. 2. It's an extra level of security that's enabled if the SAML assertion contains particularly sensitive user information or the environment dictates the need.


2 Answers

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();
    }
}
like image 58
d89761 Avatar answered Oct 16 '22 12:10

d89761


I tried to decrypt an EncryptedAssertion in different ways and ended up with this approach using https://www.nuget.org/packages/SAML2.Core (github: https://github.com/elerch/SAML2).

I followed these steps:

  1. Create a pfx-file (PKCS#12) containing both your public certificate and your private key like this:

openssl pkcs12 -export -in -inkey -out cert_key.p12

  1. Open the document with saml:EncryptedAssertion as root element

  2. Read the certificate pkcs12 container

  3. Set the document and key

  4. Decrypt the document

Complete code:

        var doc = LoadXmlDocument(@"path\to\xml\withencryptedassertion");
        var cert = new X509Certificate2(@"path\to\cert_key.p12", "<container_password>");

        var encryptedAssertion = new SAML2.Saml20EncryptedAssertion((RSA)cert.PrivateKey, doc);
        encryptedAssertion.Decrypt();
        var decryptedContent = encryptedAssertion.Assertion.InnerXml;

LoadXmlDocument is a basic file reader:

   public static XmlDocument LoadXmlDocument(string assertionFile) {
        using (var fs = File.OpenRead(assertionFile))
        {
            var document = new XmlDocument { PreserveWhitespace = true };
            document.Load(fs);
            fs.Close();
            return document;
        }
    }

I used https://developers.onelogin.com/saml/online-tools/ to generate certificates and sample data to test this code

like image 29
Morten Avatar answered Oct 16 '22 10:10

Morten