Search code examples
c#xmlsaml-2.0xml-signature

Verifying signed XML in C# with multiple signatures


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

Solution

  • 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!