I'm trying to implement a SAML SSO solution in .Net, but I'm having a problem parsing the assertion.
I have a sample assertion (looks like byte[]
data as text) and corresponding .p7b
file.
I want to load the keys from the .p7b
and decrypt the assertion to an XML document.
So far I think I'm reading the keys correctly:
// get the key data
byte[] certificateData = System.IO.File.ReadAllBytes("myKeys.p7b");
// decode the keys
var cms = new SignedCms(SubjectIdentifierType.IssuerAndSerialNumber);
cms.Decode(certificateData);
var samlCertificates = cms.Certificates;
Then I try to parse the assertion I get a problem:
// we have a keychain of X509Certificate2s, we need a collection of tokens
var certificatesAsTokens =
from X509Certificate2 cert in samlCertificates
select new X509SecurityToken(cert) as SecurityToken;
// get a token resolver
var tokens = new ReadOnlyCollection<SecurityToken>(
certificatesAsTokens.ToList());
var resolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(
tokens, true);
// get the SAML data in an XML reader
var reader = XmlReader.Create(assertionPostStream);
// use the WS Security stuff to parse the reader
var securityToken = WSSecurityTokenSerializer.
DefaultInstance.ReadToken(reader, resolver) as SamlSecurityToken;
That last statement throws an exception, stating that it can't parse the XML content.
I think this means that I'm missing a step decrypting the assertion - getting the byte[]
as text converted to a SAML format XML document.
Anyone know how to add this step? Am I missing something else?
SAML works by exchanging user information, such as logins, authentication state, identifiers, and other relevant attributes between the identity and service provider. As a result, it simplifies and secures the authentication process as the user only needs to log in once with a single set of authentication credentials.
I've figured this out - I was missing part of the SAML specification.
The assertion is sent (rather weirdly, since it isn't encrypted) as base64 data, and it was being URL encoded twice as it was sent.
So adding this step gives us a valid assertion:
// spec says "SAMLResponse="
string rawSamlData = Request["SAMLResponse"];
// the sample data sent us may be already encoded,
// which results in double encoding
if (rawSamlData.Contains('%'))
{
rawSamlData = HttpUtility.UrlDecode(rawSamlData);
}
// read the base64 encoded bytes
byte[] samlData = Convert.FromBase64String(rawSamlData);
// read back into a UTF string
string samlAssertion = Encoding.UTF8.GetString(samlData);
The authentication still isn't working, but I now have valid XML so it's a different problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With