A customer was having a problem integrating an ASP.NET application with CA Siteminder v12.52. Firstly, there was a problem with using SAML2.0 token formats. To get around that, we reconfigured SM to use SAML1.1 for this application. Still, the application was failing with error message:
ID4037: The key needed to verify the signature could not be resolved from the following security key identifier ‘SecurityKeyIdentifier
After some research it turned out that Systeme.IdentityModel.MetadataSerializer class is having a problem with fully parsing the federation metadata file provided by SM. That portion of the metadata file has the following format in SM:
<ns8:KeyDescriptor use=“signing“>
<ns4:KeyInfo Id=“SM6752f8ab2dc045dee02e91fc3e4c3d4dba0da5d201“>
<ns4:X509Data>
<ns4:X509IssuerSerial>
<ns4:X509IssuerName>CN=IDMD01PS.wcbsyst.com</ns4:X509IssuerName>
<ns4:X509SerialNumber>50492557723305036384415117878260826031</ns4:X509SerialNumber>
</ns4:X509IssuerSerial>
<ns4:X509Certificate>MIIC7DCCAdSgAwIBAgIQJfyDTK40kYZHN0r2dyZ/rzANBgkqhkiG9w0BAQUFADAfMR0wGwYDVQQDExRJRE1EMDFQUy53Y2JzeXN0LmNvbTAeFw0xNzAyMTQyMTIyMzVaFw0xODAyMTQwMDAwMDBaMB8xHTAbBgNVBAMTFElETUQwMVBTLndjYnN5c3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2UBgxp/+RcES/cwEPsTIdrlPszSrqDGN4bPpGA3XBeS/RpXyqQNfG1iiSSkdemfnAZ0QpNaMCo/5bZWHQjC842QBkP1bFcwGT9t1HQodH1LyBT0nLZkOuxXxa2ibhrv3tgBtk+nU3hHeqonejNGJxIKnUzvIOlxF39tMu8Fco3GFxTbztyBAvZQBr20G+nYn3+XJ6uL3oOMwNlihu0rqg5FsaU+hsyBPpeR+8jYwft+iYhwJb1COo1Lvx3nbuGHS260R/rTQxo9T3UcI3P4A84UROMH2QPIao6FiakTK5LMh+SSqsKOzWg3MiTSxoqLDfAmALGTPNbt27KjC5WDAWwIDAQABoyQwIjALBgNVHQ8EBAMCBDAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADggEBAAQjyiTQ+aVrNcLqujjwr+1uJ848P0D805hPkkx2QdCVA/a/cwZ4VTpSeMrPM3M2RwtJXDEq1roUePmIiylGYTbggBaOIosTHB6F7Kr1mKxdxX53fvCHWTZFc8rXv85kx/ySOAMHingrtbna1b+rlpRrBPl+l5dZ90bZZNuMBxufTPHBwGNKUQ6TRu5s5BWRDd07yM39agsvxN6Vc8xACKvVc/WKBpVm9YsbMY2FlciMcpBSXpvzblam3KOY0ESszvJEpxzdupESuFxdUzZHo+DNPqCm+vkyxUXO8/DXueb6MI5GBfXbGWmEcoWbPr4KkFYbzp1RqJOQGFLEQevrVsg=</ns4:X509Certificate>
</ns4:X509Data>
</ns4:KeyInfo>
</ns8:KeyDescriptor>
The metadata serializer was picking up the presence of the key but not the raw data and thus not creating a security key in the WS-Federation configuration object. Our solution was to create our own ConfigurationRetriever and pass it as parameter to the WsFederationAuthenticationOptions. Here are the relevant portions of the code:
1: public void ConfigureAuth(IAppBuilder app)
2: {
3: app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
4:
5: app.UseCookieAuthentication(new CookieAuthenticationOptions());
6:
7: var metadata = "https://idp.systest.worksafebc.com/affwebservices/public/FederationMetadata/idpevc3pocpartnership";
8: app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
9: {
10: Wtrealm = "https://Meraridom.onmicrosoft.com/WebApplication1b",
11: ConfigurationManager = new Microsoft.IdentityModel.Protocols.ConfigurationManager<WsFederationConfiguration>(
12: metadata, new ConfigurationRetriever(),
13: new DocumentRetriever()),
14: TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
15: {
16: ValidAudience = "https://Meraridom.onmicrosoft.com/WebApplication1b",
17: },
18: SignOutWreply = postLogoutRedirectUri,
19: });
20: }
21:
22: public class ConfigurationRetriever : Microsoft.IdentityModel.Protocols.IConfigurationRetriever<WsFederationConfiguration>
23: {
24: public async Task<WsFederationConfiguration> GetConfigurationAsync(string address, IDocumentRetriever retriever, CancellationToken cancel)
25: {
26: var conf = new WsFederationConfiguration();
27: var doc = await retriever.GetDocumentAsync(address, cancel);
28: var bytes = Encoding.UTF8.GetBytes(doc);
29: var stream = new MemoryStream(bytes);
30: var ser = new MetadataSerializer();
31: //ser.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
32: var md = (EntityDescriptor)ser.ReadMetadata(stream);
33: conf.Issuer = md.EntityId.Id;
34: foreach (var d in md.RoleDescriptors)
35: {
36: var svc = d as SecurityTokenServiceDescriptor;
37: if ((svc != null) && (svc.PassiveRequestorEndpoints.Count > 0))
38: {
39: conf.TokenEndpoint = svc.PassiveRequestorEndpoints.First().Uri.AbsoluteUri;
40: }
41: }
42: GetCerts(XDocument.Parse(doc), conf.SigningKeys);
43: return conf;
44:
45: }
46: static XNamespace dflt = "http://www.w3.org/2005/08/addressing";
47: static XNamespace ns4 = "http://www.w3.org/2000/09/xmldsig#";
48: static XNamespace ns8 = "urn:oasis:names:tc:SAML:2.0:metadata";
49: private void GetCerts(XDocument metadata, ICollection<SecurityKey> keys)
50: {
51: var roleDesc = metadata.Root.Element(ns8 + "RoleDescriptor");
52: foreach (var keyDesc in roleDesc.Elements(ns8 + "KeyDescriptor"))
53: {
54: var useAttr = keyDesc.Attribute("use");
55: if (useAttr == null) continue;
56: if (String.Compare(useAttr.Value, "signing", StringComparison.InvariantCulture) == 0)
57: {
58: var certData = keyDesc.Element(ns4 + "KeyInfo").Element(ns4 + "X509Data").Element(ns4 + "X509Certificate");
59: var data = certData.Value;
60: var bytes = new System.Text.ASCIIEncoding().GetBytes(data);
61: var cert = new X509Certificate2(bytes);
62: var key = new X509AsymmetricSecurityKey(cert);
63: keys.Add(key);
64: }
65: }
66: }
67: }
68: public class DocumentRetriever : Microsoft.IdentityModel.Protocols.IDocumentRetriever
69: {
70: public async Task<string> GetDocumentAsync(string address, CancellationToken cancel)
71: {
72: var client = new HttpClient();
73: return await client.GetStringAsync(address);
74: }
75: }
76: