Part3: Explicit ACS federation
In last post, I have shown you how to federate ACS with a custom STS. All the token acquisition (from my local STS) and forwarding (to ACS) magic was done by WSFederationHttpBinding and you never see the intermediate token (issued by your local STS). There are scenarios where you want more explicit control over this intermediate token. In this post I will show some techniques to get hold of this intermediate token and thus control its lifetime & forwarding etc.
Step 1: Get the token from our local STS.
private static SecurityToken GetTokenFromLocalSTS()
{
var localSTSBinding = new WSHttpBinding("AnnonyForCertificate");
//only public key cert. use to secure communication.
var localSTSCert = new X509Certificate2(@"MyCustomSTSPublicKey.cer");
var localSTSIdentity = new X509CertificateEndpointIdentity(localSTSCert);
var localSTSAddress = new EndpointAddress(new Uri("http://localhost:9000/STS"), localSTSIdentity);
WSTrustClient client = new WSTrustClient(localSTSBinding, localSTSAddress,TrustVersion.WSTrustFeb2005, new ClientCredentials());
RequestSecurityToken rst = new RequestSecurityToken(RequestTypeConstants.Issue);
rst.AppliesTo = new EndpointAddress("http://accesscontrol.windows.net/sts/eval01/saml for certificate/");
RequestSecurityTokenResponse rstr;
var token = client.Issue(rst, out rstr);
client.Close();
return token;
}
My local STS is configured to issue token to all annonymous callers. Here is the binding:
<binding name="AnnonyForCertificate">
<security mode="Message">
<message clientCredentialType="None" negotiateServiceCredential="false" establishSecurityContext="false"/>
</security>
</binding>
Step 2: Forward this token to ACS along with Issue request.
private static void FederateMyCustomSTS With ACS()
{
var intermediateToken = GetTokenFromLocalSTS();
// we got token from my our local STS. Forward this token to ACS with Issue request
var acsCert = GetACSCertificate();
var acsIdentity = new X509CertificateEndpointIdentity(acsCert);
var acsAddress = new EndpointAddress(new Uri("http://accesscontrol.windows.net/sts/eval01/saml for certificate"), acsIdentity);
var acsBinding = new CustomBinding("AnySamlForCertificate");
var acsClient = new WSTrustClient(acsBinding, acsAddress, TrustVersion.WSTrust13, new ClientCredentials());
acsClient = acsClient.SetIssuedToken(intermediateToken);
RequestSecurityToken rstACS = new RequestSecurityToken(RequestTypeConstants.Issue);
rstACS.AppliesTo = new EndpointAddress("http://zamd.net/");
var finalToken = acsClient.Issue(rstACS) as GenericXmlSecurityToken;
// dump SAML.
var rpCert = new X509Certificate2(@"zamdnetprivatekeycert.pfx", "a");
var saml = ExtractSAMLAssertion(finalToken, rpCert);
}
The ACS binding is as follows:
<binding name="AnySamlForCertificate">
<security authenticationMode="IssuedTokenForCertificate"
messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10"
requireSignatureConfirmation="false">
<issuedTokenParameters>
<issuer address="http://dummy" binding="basicHttpBinding"/>
</issuedTokenParameters>
</security>
<httpTransport/>
</binding>
With the above code I get back following SAML assertion (containing only one claim).
<saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<saml:Subject>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:holder-of-key</saml:ConfirmationMethod>
<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=Root Agency</X509IssuerName>
<X509SerialNumber>53391460269745460375752793295988585963</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>laW9NYJ3NHh+M3c6U1Nq/FADoAWsVc2/JZHYIHJ/qWF536ecpA12NI5orlyvzJ9D+gAK98ZsyouHm1rvQPrttpATQtilrkkyvwKK6JRQb4Ji9th8QGxA+9Yc8yKXlS+rn+lQhdGjYwH8PVxb38IlyJFydtJDMtsJID1OiXagp/w=</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
</KeyInfo>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Attribute AttributeName="action" AttributeNamespace="http://docs.oasis-open.org/wsfed/authorization/200706/claims">
<saml:AttributeValue>that's done</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
I hope you will find this post useful. In the next post, I will show how to federate ACS with Geneva Server. Stay tuned...