1

I have some XML signature verifying code that works on all examples of SAML XML that I have, except one. The one it fails on has multiple signatures. It succeeds on the first signature and fails on the second one.

Then I thought, hey what if I swap the signatures? In the first scenario signature A is first, signature B is second. The code verifies signature A and fails on signature B. If I swap them such that signature B is first, the code verifies signature B but fails on signature A! So it appears the code is able to verify both signatures independently, but fails when it's located in a certain location.

Now I know this saml response is valid, and you can see for yourself on samltool.com. The only problem is the code fails to verify the signature in some instances and I'm not sure why.

public static int isValidSignature(string encodedXml, string key)
{
    int ret = 0;

    string xml = Encoding.UTF8.GetString(Convert.FromBase64String(encodedXml));

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.PreserveWhitespace = true;
    xmlDoc.LoadXml(xml);

    X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(key));

    SignedXml signedXml = new SignedXml(xmlDoc);

    //XmlNodeList nodeList = xmlDoc.GetElementsByTagName("ds:Signature");
    NameTable nt = new NameTable();
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
    nsmgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");

    XmlNodeList nodeList = xmlDoc.SelectNodes(".//ds:Signature", nsmgr);

    if (nodeList.Count == 0)
    {
        ret = -1;
    }
    else
    {
        foreach (XmlNode node in nodeList) {
            signedXml.LoadXml((XmlElement)node);

            if (signedXml.CheckSignature(cert, true))
            {
                ret = 1;
            }
            else if(ret == 1)
            {
                ret = -2;
                break;
            }
        }
    }

    return ret;
}

The code below tests two things: The original XML, the XML with the signatures swapped. I also tested two other things but they won't fit in this question: the XML with only the outer signature (returns 1), and the XML with only the inner signature (returns 0).

string oktaKey = "MIIDpDCCAoygAwIBAgIGAVyqFlFgMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0zNjM0ODIxHDAaBgkqhkiG9w0BCQEWDWluZm9Ab2t0YS5jb20wHhcNMTcwNjE1MDQ0OTA4WhcNMjcwNjE1MDQ1MDA4WjCBkjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMzYzNDgyMRwwGgYJKoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiO3YHACAdWkG2pGZQuwtAPTLq7TUOWB0ZgC2vSVBgYWZ5juuUeIji4wh+zfaHMXiZe/wfFgC4l6fPb40Lw7f5Sur39J1vEb8EiF3qUwvMbRIFoxPsFwrgcfDoKYBcTx4VQHU/rig02VS+njzaqBL9e0RnyVoi5Ub1yeWTSq728V7NDHULm3gYHMaLqzN/z7IP64XqqSMpEE2lyeecijt2JdYkSp85al5o3wQR5j8Vr6RcBtd12koggicdLqK9Rbvg4uljSk9gGuFYvNw+2SEP+k7dbuT+uiie8mwLFkwhcOGLZWYDmGPru76ZxTpuSPAenIXMRbeTIujmuGz+qZcCwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBXMN2mKMcVDVxic9zi0LYDJIMzZkR0jQ20ksrKSvo+dFtAmAFsI29vYY2Wva/sdwbVCKHqfHKLS51CIVFwPLxzmqtZeR7WTAac23WeFtJTSl824BvWgW1zr5EYRXr4JvEZFc0kwgCldzQT0NwZG40eWX2Id0nZmjIxfNNuuH7lVXMK6yXCxa8/jF/EuowaE6DS+ZH12/INdl8O8u2Zi2v23tovNrMEs4a7dOINHWqh8vPgxKFkT2Dpcz4ry3vS4ad+9kyFK6yvtFPxM8YqHFT2ojjdVJ3IAJv8OPIi22jwMBu+M/Xl68IbYIJ3PXmxSXsDx904T0iQSnyi+G2klY5l";
string oktaSample = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ncmFwZS9zaW1wbGVzYW1sL21vZHVsZS5waHAvc2FtbC9zcC9zYW1sMi1hY3MucGhwL2RlZmF1bHQtc3AiIElEPSJpZDU4OTYwNjQ2MTQ4OTI3MTkxNDQ4MTk0NTU2IiBJblJlc3BvbnNlVG89Il9lM2VmMTkwZjQzOTk5YjZiYzE3NDAzOGIzNTQ1ODA5ZWRhYWI5MzI3YjYiIElzc3VlSW5zdGFudD0iMjAxOC0wNS0wN1QyMTozOToyMy42MTNaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly93d3cub2t0YS5jb20vZXhrZXhxMmRteVE0c3ptTWgwaDc8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+PGRzOlJlZmVyZW5jZSBVUkk9IiNpZDU4OTYwNjQ2MTQ4OTI3MTkxNDQ4MTk0NTU2Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48ZWM6SW5jbHVzaXZlTmFtZXNwYWNlcyB4bWxuczplYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIiBQcmVmaXhMaXN0PSJ4cyIvPjwvZHM6VHJhbnNmb3JtPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPlVZMEM3dStaWm1RY1FCZGJmRmZhYmlRd2dTTHVXQjJYby9TVWZrZ3EyNzA9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkZPZW9jUDNJQmh3a1dncklETDRGTjVUbFJNT3NkbjRRVFdJVzd1dTlJeXpxUFdGa3R5MzZtamc0d3dzTmFpVWc5UVV5OEl5TVNud1VMN05Sa3l5T0tqRFZEWnZCazVwVjRNUmdtSXI5K2xhdVVSc09iQy8rODNtdkEzTFBaa01SRGU3YTBET0NibENldU1ESE9lM0VZVWp1cExRSnFhb0xyc0xySlJISGxXVVI1aGZyYTJLb2NCZHhydHRyM2lvNXdNcTBrdkdvdU56ays3a3k3aVUzNEJvZjl0YzBkanRaRTRpalJhaTVQZDMwakloY3hqYlN6cDFXWG5JVit4OU8vY3czamJESFZiM0FsQjIvSjJiMXhqMFZ4YWlFdTlFMFBuem1Gc05MZkFYL3RjMy9DeU1PdkFsQ1g0TldRRjNIUGgrc1hvNEwyOUJpWjJ4MWU0aXBkdz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlEcERDQ0FveWdBd0lCQWdJR0FWeXFGbEZnTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdTTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHCkExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUVDZ3dFVDJ0MFlURVUKTUJJR0ExVUVDd3dMVTFOUFVISnZkbWxrWlhJeEV6QVJCZ05WQkFNTUNtUmxkaTB6TmpNME9ESXhIREFhQmdrcWhraUc5dzBCQ1FFVwpEV2x1Wm05QWIydDBZUzVqYjIwd0hoY05NVGN3TmpFMU1EUTBPVEE0V2hjTk1qY3dOakUxTURRMU1EQTRXakNCa2pFTE1Ba0dBMVVFCkJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTURWTmhiaUJHY21GdVkybHpZMjh4RFRBTEJnTlYKQkFvTUJFOXJkR0V4RkRBU0JnTlZCQXNNQzFOVFQxQnliM1pwWkdWeU1STXdFUVlEVlFRRERBcGtaWFl0TXpZek5EZ3lNUnd3R2dZSgpLb1pJaHZjTkFRa0JGZzFwYm1adlFHOXJkR0V1WTI5dE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCmlPM1lIQUNBZFdrRzJwR1pRdXd0QVBUTHE3VFVPV0IwWmdDMnZTVkJnWVdaNWp1dVVlSWppNHdoK3pmYUhNWGlaZS93ZkZnQzRsNmYKUGI0MEx3N2Y1U3VyMzlKMXZFYjhFaUYzcVV3dk1iUklGb3hQc0Z3cmdjZkRvS1lCY1R4NFZRSFUvcmlnMDJWUytuanphcUJMOWUwUgpueVZvaTVVYjF5ZVdUU3E3MjhWN05ESFVMbTNnWUhNYUxxek4vejdJUDY0WHFxU01wRUUybHllZWNpanQySmRZa1NwODVhbDVvM3dRClI1ajhWcjZSY0J0ZDEya29nZ2ljZExxSzlSYnZnNHVsalNrOWdHdUZZdk53KzJTRVArazdkYnVUK3VpaWU4bXdMRmt3aGNPR0xaV1kKRG1HUHJ1NzZaeFRwdVNQQWVuSVhNUmJlVEl1am11R3orcVpjQ3dJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUJYTU4ybQpLTWNWRFZ4aWM5emkwTFlESklNelprUjBqUTIwa3NyS1N2bytkRnRBbUFGc0kyOXZZWTJXdmEvc2R3YlZDS0hxZkhLTFM1MUNJVkZ3ClBMeHptcXRaZVI3V1RBYWMyM1dlRnRKVFNsODI0QnZXZ1cxenI1RVlSWHI0SnZFWkZjMGt3Z0NsZHpRVDBOd1pHNDBlV1gySWQwbloKbWpJeGZOTnV1SDdsVlhNSzZ5WEN4YTgvakYvRXVvd2FFNkRTK1pIMTIvSU5kbDhPOHUyWmkydjIzdG92TnJNRXM0YTdkT0lOSFdxaAo4dlBneEtGa1QyRHBjejRyeTN2UzRhZCs5a3lGSzZ5dnRGUHhNOFlxSEZUMm9qamRWSjNJQUp2OE9QSWkyMmp3TUJ1K00vWGw2OEliCllJSjNQWG14U1hzRHg5MDRUMGlRU255aStHMmtsWTVsPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJpZDU4OTYwNjQ2MTQ5MzI0MjY4MTYzMjQ3OTYiIElzc3VlSW5zdGFudD0iMjAxOC0wNS0wN1QyMTozOToyMy42MTNaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWwyOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly93d3cub2t0YS5jb20vZXhrZXhxMmRteVE0c3ptTWgwaDc8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+PGRzOlJlZmVyZW5jZSBVUkk9IiNpZDU4OTYwNjQ2MTQ5MzI0MjY4MTYzMjQ3OTYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjxlYzpJbmNsdXNpdmVOYW1lc3BhY2VzIHhtbG5zOmVjPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIFByZWZpeExpc3Q9InhzIi8+PC9kczpUcmFuc2Zvcm0+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+OFN4a1A5MDNhRk5XcUNkUTYxTUJDWGErZVMrelNBM0U3ZVN3NGxoVHRTST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+YURtOEx0bHE1VFVFaUJDMnp2eUlzSzREejF0aDZ6KzhNeXdWSEFiWElCNEg0bGIrL0VUd2VCamJJZkxQNytsdUVSbjFGZ1Y4NWpEQkpCNXpBK294c0UvVVZpMnBpdUZqTHdkOVhtTno3SVNhYlZqU09qZEd0Q0o1SGJPM2RIaXEvd0E4OGpTOWlDaHJFS2J5c2V6N2RzRXFEOHMrekJKS2tvY294ajlCUmIrMHJ0L25uaWNuK1dqWVdHRjRWdnZTdGl3V2NHWitGSjZxZ3ZwY1hJUFNrSWt2ZXY3N1g0R2ljWTk5eFNVNHlkTnVGZlg3c2kvMytVRWs3T3BDbzJnQ1poOUpxcEZmTlNxNzFkYWQ1QkRENHMyNTVvaWRhSjIrRTcwWTVLVmI3bWNFNHJmNkVCcUd6ZlpIYUZZZ0xzZnpOS2wxc05CT3lubHFlVXhvMVNDcStBPT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURwRENDQW95Z0F3SUJBZ0lHQVZ5cUZsRmdNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1JR1NNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUcKQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVXTUJRR0ExVUVCd3dOVTJGdUlFWnlZVzVqYVhOamJ6RU5NQXNHQTFVRUNnd0VUMnQwWVRFVQpNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RXpBUkJnTlZCQU1NQ21SbGRpMHpOak0wT0RJeEhEQWFCZ2txaGtpRzl3MEJDUUVXCkRXbHVabTlBYjJ0MFlTNWpiMjB3SGhjTk1UY3dOakUxTURRME9UQTRXaGNOTWpjd05qRTFNRFExTURBNFdqQ0JrakVMTUFrR0ExVUUKQmhNQ1ZWTXhFekFSQmdOVkJBZ01Da05oYkdsbWIzSnVhV0V4RmpBVUJnTlZCQWNNRFZOaGJpQkdjbUZ1WTJselkyOHhEVEFMQmdOVgpCQW9NQkU5cmRHRXhGREFTQmdOVkJBc01DMU5UVDFCeWIzWnBaR1Z5TVJNd0VRWURWUVFEREFwa1pYWXRNell6TkRneU1Sd3dHZ1lKCktvWklodmNOQVFrQkZnMXBibVp2UUc5cmRHRXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUEKaU8zWUhBQ0FkV2tHMnBHWlF1d3RBUFRMcTdUVU9XQjBaZ0MydlNWQmdZV1o1anV1VWVJamk0d2gremZhSE1YaVplL3dmRmdDNGw2ZgpQYjQwTHc3ZjVTdXIzOUoxdkViOEVpRjNxVXd2TWJSSUZveFBzRndyZ2NmRG9LWUJjVHg0VlFIVS9yaWcwMlZTK25qemFxQkw5ZTBSCm55Vm9pNVViMXllV1RTcTcyOFY3TkRIVUxtM2dZSE1hTHF6Ti96N0lQNjRYcXFTTXBFRTJseWVlY2lqdDJKZFlrU3A4NWFsNW8zd1EKUjVqOFZyNlJjQnRkMTJrb2dnaWNkTHFLOVJidmc0dWxqU2s5Z0d1Rll2TncrMlNFUCtrN2RidVQrdWlpZThtd0xGa3doY09HTFpXWQpEbUdQcnU3Nlp4VHB1U1BBZW5JWE1SYmVUSXVqbXVHeitxWmNDd0lEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQlhNTjJtCktNY1ZEVnhpYzl6aTBMWURKSU16WmtSMGpRMjBrc3JLU3ZvK2RGdEFtQUZzSTI5dllZMld2YS9zZHdiVkNLSHFmSEtMUzUxQ0lWRncKUEx4em1xdFplUjdXVEFhYzIzV2VGdEpUU2w4MjRCdldnVzF6cjVFWVJYcjRKdkVaRmMwa3dnQ2xkelFUME53Wkc0MGVXWDJJZDBuWgptakl4Zk5OdXVIN2xWWE1LNnlYQ3hhOC9qRi9FdW93YUU2RFMrWkgxMi9JTmRsOE84dTJaaTJ2MjN0b3ZOck1FczRhN2RPSU5IV3FoCjh2UGd4S0ZrVDJEcGN6NHJ5M3ZTNGFkKzlreUZLNnl2dEZQeE04WXFIRlQyb2pqZFZKM0lBSnY4T1BJaTIyandNQnUrTS9YbDY4SWIKWUlKM1BYbXhTWHNEeDkwNFQwaVFTbnlpK0cya2xZNWw8L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDI6U3ViamVjdCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OnVuc3BlY2lmaWVkIj5mYWtlQGZha2UubmV0PC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfZTNlZjE5MGY0Mzk5OWI2YmMxNzQwMzhiMzU0NTgwOWVkYWFiOTMyN2I2IiBOb3RPbk9yQWZ0ZXI9IjIwMTgtMDUtMDdUMjE6NDQ6MjMuNjEzWiIgUmVjaXBpZW50PSJodHRwczovL2dyYXBlL3NpbXBsZXNhbWwvbW9kdWxlLnBocC9zYW1sL3NwL3NhbWwyLWFjcy5waHAvZGVmYXVsdC1zcCIvPjwvc2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWwyOlN1YmplY3Q+PHNhbWwyOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDE4LTA1LTA3VDIxOjM0OjIzLjYxM1oiIE5vdE9uT3JBZnRlcj0iMjAxOC0wNS0wN1QyMTo0NDoyMy42MTNaIiB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWwyOkF1ZGllbmNlPmh0dHBzOi8vZ3JhcGUvc2ltcGxlc2FtbC9tb2R1bGUucGhwL3NhbWwvc3AvbWV0YWRhdGEucGhwL2RlZmF1bHQtc3A8L3NhbWwyOkF1ZGllbmNlPjwvc2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWwyOkNvbmRpdGlvbnM+PHNhbWwyOkF1dGhuU3RhdGVtZW50IEF1dGhuSW5zdGFudD0iMjAxOC0wNS0wN1QyMTozODoyNi4zMzRaIiBTZXNzaW9uSW5kZXg9Il9lM2VmMTkwZjQzOTk5YjZiYzE3NDAzOGIzNTQ1ODA5ZWRhYWI5MzI3YjYiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXV0aG5Db250ZXh0PjxzYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvc2FtbDI6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sMjpBdXRobkNvbnRleHQ+PC9zYW1sMjpBdXRoblN0YXRlbWVudD48c2FtbDI6QXR0cmlidXRlU3RhdGVtZW50IHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXR0cmlidXRlIE5hbWU9IkVtYWlsQWRkcmVzcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1bnNwZWNpZmllZCI+PHNhbWwyOkF0dHJpYnV0ZVZhbHVlIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnR5cGU9InhzOnN0cmluZyI+ZmFrZUBmYWtlLm5ldDwvc2FtbDI6QXR0cmlidXRlVmFsdWU+PC9zYW1sMjpBdHRyaWJ1dGU+PC9zYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQ+PC9zYW1sMjpBc3NlcnRpb24+PC9zYW1sMnA6UmVzcG9uc2U+";
string oktaSample2 = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ncmFwZS9zaW1wbGVzYW1sL21vZHVsZS5waHAvc2FtbC9zcC9zYW1sMi1hY3MucGhwL2RlZmF1bHQtc3AiIElEPSJpZDU4OTYwNjQ2MTQ4OTI3MTkxNDQ4MTk0NTU2IiBJblJlc3BvbnNlVG89Il9lM2VmMTkwZjQzOTk5YjZiYzE3NDAzOGIzNTQ1ODA5ZWRhYWI5MzI3YjYiIElzc3VlSW5zdGFudD0iMjAxOC0wNS0wN1QyMTozOToyMy42MTNaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHA6Ly93d3cub2t0YS5jb20vZXhrZXhxMmRteVE0c3ptTWgwaDc8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+PGRzOlJlZmVyZW5jZSBVUkk9IiNpZDU4OTYwNjQ2MTQ5MzI0MjY4MTYzMjQ3OTYiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiPjxlYzpJbmNsdXNpdmVOYW1lc3BhY2VzIHhtbG5zOmVjPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIFByZWZpeExpc3Q9InhzIi8+PC9kczpUcmFuc2Zvcm0+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+OFN4a1A5MDNhRk5XcUNkUTYxTUJDWGErZVMrelNBM0U3ZVN3NGxoVHRTST08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+YURtOEx0bHE1VFVFaUJDMnp2eUlzSzREejF0aDZ6KzhNeXdWSEFiWElCNEg0bGIrL0VUd2VCamJJZkxQNytsdUVSbjFGZ1Y4NWpEQkpCNXpBK294c0UvVVZpMnBpdUZqTHdkOVhtTno3SVNhYlZqU09qZEd0Q0o1SGJPM2RIaXEvd0E4OGpTOWlDaHJFS2J5c2V6N2RzRXFEOHMrekJKS2tvY294ajlCUmIrMHJ0L25uaWNuK1dqWVdHRjRWdnZTdGl3V2NHWitGSjZxZ3ZwY1hJUFNrSWt2ZXY3N1g0R2ljWTk5eFNVNHlkTnVGZlg3c2kvMytVRWs3T3BDbzJnQ1poOUpxcEZmTlNxNzFkYWQ1QkRENHMyNTVvaWRhSjIrRTcwWTVLVmI3bWNFNHJmNkVCcUd6ZlpIYUZZZ0xzZnpOS2wxc05CT3lubHFlVXhvMVNDcStBPT08L2RzOlNpZ25hdHVyZVZhbHVlPjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURwRENDQW95Z0F3SUJBZ0lHQVZ5cUZsRmdNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1JR1NNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUcNCkExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlUyRnVJRVp5WVc1amFYTmpiekVOTUFzR0ExVUVDZ3dFVDJ0MFlURVUNCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhFekFSQmdOVkJBTU1DbVJsZGkwek5qTTBPREl4SERBYUJna3Foa2lHOXcwQkNRRVcNCkRXbHVabTlBYjJ0MFlTNWpiMjB3SGhjTk1UY3dOakUxTURRME9UQTRXaGNOTWpjd05qRTFNRFExTURBNFdqQ0JrakVMTUFrR0ExVUUNCkJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTURWTmhiaUJHY21GdVkybHpZMjh4RFRBTEJnTlYNCkJBb01CRTlyZEdFeEZEQVNCZ05WQkFzTUMxTlRUMUJ5YjNacFpHVnlNUk13RVFZRFZRUUREQXBrWlhZdE16WXpORGd5TVJ3d0dnWUoNCktvWklodmNOQVFrQkZnMXBibVp2UUc5cmRHRXVZMjl0TUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUENCmlPM1lIQUNBZFdrRzJwR1pRdXd0QVBUTHE3VFVPV0IwWmdDMnZTVkJnWVdaNWp1dVVlSWppNHdoK3pmYUhNWGlaZS93ZkZnQzRsNmYNClBiNDBMdzdmNVN1cjM5SjF2RWI4RWlGM3FVd3ZNYlJJRm94UHNGd3JnY2ZEb0tZQmNUeDRWUUhVL3JpZzAyVlMrbmp6YXFCTDllMFINCm55Vm9pNVViMXllV1RTcTcyOFY3TkRIVUxtM2dZSE1hTHF6Ti96N0lQNjRYcXFTTXBFRTJseWVlY2lqdDJKZFlrU3A4NWFsNW8zd1ENClI1ajhWcjZSY0J0ZDEya29nZ2ljZExxSzlSYnZnNHVsalNrOWdHdUZZdk53KzJTRVArazdkYnVUK3VpaWU4bXdMRmt3aGNPR0xaV1kNCkRtR1BydTc2WnhUcHVTUEFlbklYTVJiZVRJdWptdUd6K3FaY0N3SURBUUFCTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFCWE1OMm0NCktNY1ZEVnhpYzl6aTBMWURKSU16WmtSMGpRMjBrc3JLU3ZvK2RGdEFtQUZzSTI5dllZMld2YS9zZHdiVkNLSHFmSEtMUzUxQ0lWRncNClBMeHptcXRaZVI3V1RBYWMyM1dlRnRKVFNsODI0QnZXZ1cxenI1RVlSWHI0SnZFWkZjMGt3Z0NsZHpRVDBOd1pHNDBlV1gySWQwbloNCm1qSXhmTk51dUg3bFZYTUs2eVhDeGE4L2pGL0V1b3dhRTZEUytaSDEyL0lOZGw4Tzh1MlppMnYyM3Rvdk5yTUVzNGE3ZE9JTkhXcWgNCjh2UGd4S0ZrVDJEcGN6NHJ5M3ZTNGFkKzlreUZLNnl2dEZQeE04WXFIRlQyb2pqZFZKM0lBSnY4T1BJaTIyandNQnUrTS9YbDY4SWINCllJSjNQWG14U1hzRHg5MDRUMGlRU255aStHMmtsWTVsPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWwycDpTdGF0dXMgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxzYW1sMnA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1sMnA6U3RhdHVzPjxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJpZDU4OTYwNjQ2MTQ5MzI0MjY4MTYzMjQ3OTYiIElzc3VlSW5zdGFudD0iMjAxOC0wNS0wN1QyMTozOToyMy42MTNaIiBWZXJzaW9uPSIyLjAiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSI+PHNhbWwyOklzc3VlciBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPmh0dHA6Ly93d3cub2t0YS5jb20vZXhrZXhxMmRteVE0c3ptTWgwaDc8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+PGRzOlJlZmVyZW5jZSBVUkk9IiNpZDU4OTYwNjQ2MTQ4OTI3MTkxNDQ4MTk0NTU2Ij48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIj48ZWM6SW5jbHVzaXZlTmFtZXNwYWNlcyB4bWxuczplYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIiBQcmVmaXhMaXN0PSJ4cyIvPjwvZHM6VHJhbnNmb3JtPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPlVZMEM3dStaWm1RY1FCZGJmRmZhYmlRd2dTTHVXQjJYby9TVWZrZ3EyNzA9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPkZPZW9jUDNJQmh3a1dncklETDRGTjVUbFJNT3NkbjRRVFdJVzd1dTlJeXpxUFdGa3R5MzZtamc0d3dzTmFpVWc5UVV5OEl5TVNud1VMN05Sa3l5T0tqRFZEWnZCazVwVjRNUmdtSXI5K2xhdVVSc09iQy8rODNtdkEzTFBaa01SRGU3YTBET0NibENldU1ESE9lM0VZVWp1cExRSnFhb0xyc0xySlJISGxXVVI1aGZyYTJLb2NCZHhydHRyM2lvNXdNcTBrdkdvdU56ays3a3k3aVUzNEJvZjl0YzBkanRaRTRpalJhaTVQZDMwakloY3hqYlN6cDFXWG5JVit4OU8vY3czamJESFZiM0FsQjIvSjJiMXhqMFZ4YWlFdTlFMFBuem1Gc05MZkFYL3RjMy9DeU1PdkFsQ1g0TldRRjNIUGgrc1hvNEwyOUJpWjJ4MWU0aXBkdz09PC9kczpTaWduYXR1cmVWYWx1ZT48ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlEcERDQ0FveWdBd0lCQWdJR0FWeXFGbEZnTUEwR0NTcUdTSWIzRFFFQkN3VUFNSUdTTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHDQpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVDQpNQklHQTFVRUN3d0xVMU5QVUhKdmRtbGtaWEl4RXpBUkJnTlZCQU1NQ21SbGRpMHpOak0wT0RJeEhEQWFCZ2txaGtpRzl3MEJDUUVXDQpEV2x1Wm05QWIydDBZUzVqYjIwd0hoY05NVGN3TmpFMU1EUTBPVEE0V2hjTk1qY3dOakUxTURRMU1EQTRXakNCa2pFTE1Ba0dBMVVFDQpCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhGakFVQmdOVkJBY01EVk5oYmlCR2NtRnVZMmx6WTI4eERUQUxCZ05WDQpCQW9NQkU5cmRHRXhGREFTQmdOVkJBc01DMU5UVDFCeWIzWnBaR1Z5TVJNd0VRWURWUVFEREFwa1pYWXRNell6TkRneU1Sd3dHZ1lKDQpLb1pJaHZjTkFRa0JGZzFwYm1adlFHOXJkR0V1WTI5dE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBDQppTzNZSEFDQWRXa0cycEdaUXV3dEFQVExxN1RVT1dCMFpnQzJ2U1ZCZ1lXWjVqdXVVZUlqaTR3aCt6ZmFITVhpWmUvd2ZGZ0M0bDZmDQpQYjQwTHc3ZjVTdXIzOUoxdkViOEVpRjNxVXd2TWJSSUZveFBzRndyZ2NmRG9LWUJjVHg0VlFIVS9yaWcwMlZTK25qemFxQkw5ZTBSDQpueVZvaTVVYjF5ZVdUU3E3MjhWN05ESFVMbTNnWUhNYUxxek4vejdJUDY0WHFxU01wRUUybHllZWNpanQySmRZa1NwODVhbDVvM3dRDQpSNWo4VnI2UmNCdGQxMmtvZ2dpY2RMcUs5UmJ2ZzR1bGpTazlnR3VGWXZOdysyU0VQK2s3ZGJ1VCt1aWllOG13TEZrd2hjT0dMWldZDQpEbUdQcnU3Nlp4VHB1U1BBZW5JWE1SYmVUSXVqbXVHeitxWmNDd0lEQVFBQk1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQlhNTjJtDQpLTWNWRFZ4aWM5emkwTFlESklNelprUjBqUTIwa3NyS1N2bytkRnRBbUFGc0kyOXZZWTJXdmEvc2R3YlZDS0hxZkhLTFM1MUNJVkZ3DQpQTHh6bXF0WmVSN1dUQWFjMjNXZUZ0SlRTbDgyNEJ2V2dXMXpyNUVZUlhyNEp2RVpGYzBrd2dDbGR6UVQwTndaRzQwZVdYMklkMG5aDQptakl4Zk5OdXVIN2xWWE1LNnlYQ3hhOC9qRi9FdW93YUU2RFMrWkgxMi9JTmRsOE84dTJaaTJ2MjN0b3ZOck1FczRhN2RPSU5IV3FoDQo4dlBneEtGa1QyRHBjejRyeTN2UzRhZCs5a3lGSzZ5dnRGUHhNOFlxSEZUMm9qamRWSjNJQUp2OE9QSWkyMmp3TUJ1K00vWGw2OEliDQpZSUozUFhteFNYc0R4OTA0VDBpUVNueWkrRzJrbFk1bDwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sMjpTdWJqZWN0IHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6dW5zcGVjaWZpZWQiPmZha2VAZmFrZS5uZXQ8L3NhbWwyOk5hbWVJRD48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89Il9lM2VmMTkwZjQzOTk5YjZiYzE3NDAzOGIzNTQ1ODA5ZWRhYWI5MzI3YjYiIE5vdE9uT3JBZnRlcj0iMjAxOC0wNS0wN1QyMTo0NDoyMy42MTNaIiBSZWNpcGllbnQ9Imh0dHBzOi8vZ3JhcGUvc2ltcGxlc2FtbC9tb2R1bGUucGhwL3NhbWwvc3Avc2FtbDItYWNzLnBocC9kZWZhdWx0LXNwIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTgtMDUtMDdUMjE6MzQ6MjMuNjEzWiIgTm90T25PckFmdGVyPSIyMDE4LTA1LTA3VDIxOjQ0OjIzLjYxM1oiIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDI6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly9ncmFwZS9zaW1wbGVzYW1sL21vZHVsZS5waHAvc2FtbC9zcC9tZXRhZGF0YS5waHAvZGVmYXVsdC1zcDwvc2FtbDI6QXVkaWVuY2U+PC9zYW1sMjpBdWRpZW5jZVJlc3RyaWN0aW9uPjwvc2FtbDI6Q29uZGl0aW9ucz48c2FtbDI6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE4LTA1LTA3VDIxOjM4OjI2LjMzNFoiIFNlc3Npb25JbmRleD0iX2UzZWYxOTBmNDM5OTliNmJjMTc0MDM4YjM1NDU4MDllZGFhYjkzMjdiNiIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdXRobkNvbnRleHQ+PHNhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkUHJvdGVjdGVkVHJhbnNwb3J0PC9zYW1sMjpBdXRobkNvbnRleHRDbGFzc1JlZj48L3NhbWwyOkF1dGhuQ29udGV4dD48L3NhbWwyOkF1dGhuU3RhdGVtZW50PjxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxzYW1sMjpBdHRyaWJ1dGUgTmFtZT0iRW1haWxBZGRyZXNzIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVuc3BlY2lmaWVkIj48c2FtbDI6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5mYWtlQGZha2UubmV0PC9zYW1sMjpBdHRyaWJ1dGVWYWx1ZT48L3NhbWwyOkF0dHJpYnV0ZT48L3NhbWwyOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=";

Debug.WriteLine(MyXml2.isValidSignature(oktaSample, oktaKey)); // prints -2
Debug.WriteLine(MyXml2.isValidSignature(oktaSample2, oktaKey)); // prints -2
7
  • Your logic is wrong. First it is checking every node for a signature. Second if signature is false (which will happen when you are checking a non signed element) and you already found a signature ( ret ==1) then you will always return a -2. Commented May 9, 2018 at 16:34
  • I don't understand. I'm only selecting signature nodes. This code works for XML that contains only the first signature. It only fails when there are multiple signatures. Commented May 9, 2018 at 16:39
  • You are correct. Didn't notice you were only checking signature nodes. Are you using same key for both signatures. You code is getting only one key. Commented May 9, 2018 at 17:07
  • Yeah they both use the same key. If I swap the signatures I get the same result: the first signature it finds is verified, the second fails. So I can independently verify them by swapping and only checking the first one. But that seems like a hacky hack. Commented May 9, 2018 at 17:11
  • See if adding following before each signature : signedXml = new SignedXml(xmlDoc); Commented May 9, 2018 at 17:14

2 Answers 2

3

Andrew! I sure answer a lot of your questions.

Okay, first off, the second example won't work because you modified the XML in such a way that the signature won't match.

So upon Googling tips on debugging this code, I stumbled across this page which describes how to see the logging for SignedXml. The meat is to add this to the App.config under the tag:

<system.diagnostics>
  <sources>
    <source name="System.Security.Cryptography.Xml.SignedXml" switchName="XmlDsigLogSwitch">
      <listeners>
        <add name="logFile" />
      </listeners>
    </source>
  </sources>
  <switches>
    <add name="XmlDsigLogSwitch" value="Verbose" />
  </switches>
  <sharedListeners>
    <add name="logFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="XmlDsigLog.txt"/>
  </sharedListeners>
  <trace autoflush="true">
    <listeners>
      <add name="logFile" />
    </listeners>
  </trace>
</system.diagnostics>

From this and your code I was able to deduce that the canonicalization was not working correctly for the second signature. Specifically, it was failing to remove the signature tag itself. This is important in verifying the signature because the signature itself is not part of the signed information. If it was, you would be here until the sun burns out trying to find a signature that contains itself.

Now I believe this is a bug in the C# implementation, but I'm not as smart as the people who write C# so I could be wrong. But here is a workaround. I added my own class that extends XmlDsigExcC14NTransform, which was suggested by several Google results but for a slightly different problem. In that class's LoadInput method, I remove the node. From there, everything worked happily!

public class SigKillXmlDsigExcC14NTransform : XmlDsigExcC14NTransform
{
    public SigKillXmlDsigExcC14NTransform() { }

    public override void LoadInput(Object obj)
    {
        XmlElement root = ((XmlDocument)obj).DocumentElement;
        NameTable nt = new NameTable();
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
        nsmgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
        XmlNodeList childSignatures = root.SelectNodes("./ds:Signature", nsmgr);

        // Sometimes C# fails to remove the child node.  Let's hold his hand and do it for him.
        foreach (XmlNode oneChild in childSignatures)
        {
            oneChild.ParentNode.RemoveChild(oneChild);
        }

        base.LoadInput(obj);
    }
}

From there you add a single line to the original code and the rest remains the same.

CryptoConfig.AddAlgorithm(typeof(SigKillXmlDsigExcC14NTransform), "http://www.w3.org/2001/10/xml-exc-c14n#");

And now the first example works!

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for this. I had the exact same problem and your fix works. Did you ever find an explanation as to why the transform fails to remove the signature in the first place?
I think it's just a bug. I reported it to MS and I'm pretty sure it's just sitting in a backlog somewhere never to be fixed.
Thanks! Was running into an issue where a SAML response had a digital signature for the Response element and the Assertion element. If it only had one signature in the document, it worked fine.
1

Thanks for the finding. It solved my saml validation problem! An alternative workaround without custom transform:

  var signedXml = new SignedXml(doc);
  var isValid = doc.GetElementsByTagName("Signature", "http://www.w3.org/2000/09/xmldsig#")
                      .OfType<XmlElement>()
                      .ToArray()
                      .All(e =>
                      {
                          //workaround - remove the signature element here.
                          e.ParentNode.RemoveChild(e);
                          signedXml.LoadXml(e);
                          return signedXml.CheckSignature(cert, true);
                      });

Note: the original xml document object is changed (signature after this call).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.