ExtractSAMLAssertion
Posted by zamd on March 10, 2010
Few people have asked me for the source code of ExtractSAMLAssertion helper method/class, which I have used in my article and blog posts. So here you go…
Please note this is just raw test code and doesn’t have any error checking and other related goodies.
class Program
{
static void Main(string[] args)
{
var binding = new WSHttpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
binding.Security.Message.EstablishSecurityContext = false;
var fac = new WSTrustChannelFactory(binding, “http://localhost:9000/STS”);
fac.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
fac.Open();
var rst = new RequestSecurityToken {
AppliesTo = new EndpointAddress(“http://localhost”),
RequestType = WSTrustFeb2005Constants.RequestTypes.Issue
};
var token = fac.CreateChannel().Issue(rst) as GenericXmlSecurityToken;
var rpCert = new X509Certificate2(“localhost.pfx”, “a”);
var xml = ExtractSAMLAssertion(token, rpCert);
}
private static string ExtractSAMLAssertion(GenericXmlSecurityToken token, X509Certificate2 rpCert)
{
string txtAssertion = string.Empty;
var handlerCol = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
var han = handlerCol.OfType<EncryptedSecurityTokenHandler>().FirstOrDefault();
if (han != null)
{
han.Configuration.ServiceTokenResolver = new EncryptedKeyResolver(rpCert);
var sr = new StringReader(token.TokenXml.OuterXml);
var rdr2 = XmlReader.Create(sr);
var rtok = han.ReadToken(rdr2) as SamlSecurityToken;
var stat = rtok.Assertion.Statements[0];
var ms = new MemoryStream();
var memWriter = XmlDictionaryWriter.CreateTextWriter(ms);
stat.WriteXml(memWriter, new SamlSerializer(), new WSSecurityTokenSerializer());
memWriter.Flush();
ms.Seek(0, SeekOrigin.Begin);
txtAssertion = new StreamReader(ms).ReadToEnd();
}
return txtAssertion;
}
}
class EncryptedKeyResolver : SecurityTokenResolver
{
// Relying party certificate – must hold private key.
X509Certificate2 rpCert;
public EncryptedKeyResolver(X509Certificate2 rpCert)
{
this.rpCert = rpCert;
}
protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)
{
key = null;
try
{
var ekec = keyIdentifierClause as EncryptedKeyIdentifierClause;
if (ekec != null)
{
switch (ekec.EncryptionMethod)
{
case “http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p”:
{
var encKey = ekec.GetEncryptedKey();
var rsa = rpCert.PrivateKey as RSACryptoServiceProvider;
var decKey = rsa.Decrypt(encKey, true);
key = new InMemorySymmetricSecurityKey(decKey);
return true;
}
}
var data = ekec.GetEncryptedKey();
var id = ekec.EncryptingKeyIdentifier;
}
}
catch (Exception eexx)
{
Console.WriteLine(eexx);
throw eexx;
}
return false;
}
protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)
{
throw new NotImplementedException();
}
protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)
{
throw new NotImplementedException();
}
}
Email
Twitter
LinkedIn
Facebook
Sid said
Hi Zulfiqar, Nice post.
I have a question – does your local STS sign the token anyhow? Or is it unsigned?
I am trying your sample code on a token issued by Live IdP and I am getting the following exception:
Microsoft.IdentityModel.Protocols.XmlSignature.SignatureVerificationFailedException was unhandled
Message=ID4037: The key needed to verify the signature could not be resolved from the following security key identifier ‘SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 1,
Clause[0] = X509SubjectKeyIdentifierClause(SKI = 0x7FA27C60886C4333E450D30EA1D58C5329A80341)
)
‘. Ensure that the SecurityTokenResolver is populated with the required key.
Cheers,
-Sid
zamd said
Hi, Yes local STS signs AND encrypt the token.
Not sure why are you getting signature validation exception. Signature validation is usually done as part of SAML token validation and my code doesn’t do that.
It simply returns the assertion as a string.
Sid said
Hmm, not sure myself why signature validation is being done here.
But the SKI in the exception error message above (0x7FA27C60886C4333E450D30EA1D58C5329A80341) is the SKI of Live’s certificate – I got Live’s public cert from its Fed-metadata and verified this.
Do you know how I would set the SecurityToken derived from the issuer cert onto the SecurityTokenResolver?
zamd said
Which source code are you using? Which Live IdP endpoint are you using?