public void Saml2LogoutRequest_FromXml()
        {
            var xmlData =
                @"<saml2p:LogoutRequest xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
 ID=""d2b7c388cec36fa7c39c28fd298644a8"" Version=""2.0"" IssueInstant=""2004-01-21T19:00:49Z""
 Destination=""http://idp.example.com/logout"">
 <saml2:Issuer>http://sp.example.com/</saml2:Issuer>
 <saml2:NameID Format=""urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"">005a06e0-ad82-110d-a556-004005b13a2b</saml2:NameID>
 <saml2p:SessionIndex>SessionId</saml2p:SessionIndex>
</saml2p:LogoutRequest>";

            var xmlDoc = XmlHelpers.FromString(xmlData);

            var subject = Saml2LogoutRequest.FromXml(xmlDoc.DocumentElement);

            var expected = new Saml2LogoutRequest(new Saml2Id("d2b7c388cec36fa7c39c28fd298644a8"))
            {
                DestinationUrl = new Uri("http://idp.example.com/logout"),
                Issuer         = new EntityId("http://sp.example.com/"),
                NameId         = new Saml2NameIdentifier(
                    "005a06e0-ad82-110d-a556-004005b13a2b",
                    new Uri(NameIdFormat.Persistent.GetUri().AbsoluteUri)),
                SessionIndex = "SessionId",
            };

            subject.ShouldBeEquivalentTo(expected);
        }
Ejemplo n.º 2
0
        public void XmlHelpers_IsSignedBy_FalseOnMissingSignature()
        {
            var xml    = "<xml ID=\"someID\"><content>text</content></xml>";
            var xmlDoc = XmlHelpers.FromString(xml);

            xmlDoc.DocumentElement.IsSignedBy(SignedXmlHelper.TestCert).Should().BeFalse();
        }
Ejemplo n.º 3
0
        public void Saml2ArtifactResponse_GetMessageThrowsOnStatusNotSuccess()
        {
            var xml =
                @"<samlp:ArtifactResponse
  xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
  xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
  ID=""_FQvGknDfws2Z"" Version=""2.0""
  InResponseTo=""_6c3a4f8b9c2d""
  IssueInstant=""2004-01-21T19:00:49Z"">
  <Issuer>https://IdentityProvider.com/SAML</Issuer>
  <ds:Signature xmlns:ds=""http://www.w3.org/2000/09/xmldsig#"">Not parsed...</ds:Signature>
  <samlp:Extensions>Extended data</samlp:Extensions>
  <samlp:Status>
    <samlp:StatusCode
      Value=""urn:oasis:names:tc:SAML:2.0:status:Responder""/>
  </samlp:Status>
  <samlp:LogoutRequest ID=""d2b7c388cec36fa7c39c28fd298644a8""
    IssueInstant=""2004-01-21T19:00:49Z""
    Version=""2.0"">
    <Issuer>https://IdentityProvider.com/SAML</Issuer>
    <NameID Format=""urn:oasis:names:tc:SAML:2.0:nameidformat:persistent"">005a06e0-ad82-110d-a556-004005b13a2b</NameID>
    <samlp:SessionIndex>1</samlp:SessionIndex>
  </samlp:LogoutRequest>
</samlp:ArtifactResponse>";

            var xmlDoc = XmlHelpers.FromString(xml);

            var subject = new Saml2ArtifactResponse(xmlDoc.DocumentElement);

            subject.Status.Should().Be(Saml2StatusCode.Responder);
            subject.Invoking(s => s.GetMessage())
            .ShouldThrow <UnsuccessfulSamlOperationException>()
            .And.Message.Should().Be("Artifact resolution returned status Responder, can't extract message.");
        }
        public void Saml2LogoutResponse_AppendTo_AllSupported()
        {
            var subject = new Saml2LogoutResponse(Saml2StatusCode.Requester)
            {
                Issuer         = new EntityId("https://ServiceProvider.com/SAML"),
                DestinationUrl = new Uri("https://IdentityProvider.com/Logout"),
                InResponseTo   = new Saml2Id()
            };

            var expectedXml =
                $@"<samlp:LogoutResponse xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
                    xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
                    ID=""{subject.Id}""
                    InResponseTo=""{subject.InResponseTo.Value}""
                    IssueInstant=""{subject.IssueInstant.ToSaml2DateTimeString()}"" Version=""2.0""
                    Destination=""https://IdentityProvider.com/Logout"">
                    <Issuer>https://ServiceProvider.com/SAML</Issuer>
                    <samlp:Status>
                        <samlp:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester""/>
                    </samlp:Status>
                </samlp:LogoutResponse>";

            var expectedElement = XmlHelpers.FromString(expectedXml).DocumentElement;

            var xmlDoc = new XmlDocument();

            subject.AppendTo(xmlDoc);

            xmlDoc.DocumentElement.Should().BeEquivalentTo(expectedElement, "XML should be full LogoutResponse");
        }
Ejemplo n.º 5
0
        public void XmlHelpers_IsSignedBy_ThrowsOnDualReferencesInSignature()
        {
            var xml = "<xml ID=\"myxml\" />";

            var xmlDoc = XmlHelpers.FromString(xml);

            var signedXml = new SignedXml(xmlDoc);

            signedXml.SigningKey = (RSACryptoServiceProvider)SignedXmlHelper.TestCert.PrivateKey;
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

            var ref1 = new Reference {
                Uri = "#myxml"
            };

            ref1.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            ref1.AddTransform(new XmlDsigExcC14NTransform());
            signedXml.AddReference(ref1);

            var ref2 = new Reference {
                Uri = "#myxml"
            };

            ref2.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            ref2.AddTransform(new XmlDsigExcC14NTransform());
            signedXml.AddReference(ref2);

            signedXml.ComputeSignature();
            xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signedXml.GetXml(), true));

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("Multiple references for Xml signatures are not allowed.");
        }
Ejemplo n.º 6
0
        public void Saml2ArtifactResponse_GetMessage()
        {
            var xml =
                @"<samlp:ArtifactResponse
  xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
  xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
  ID=""_FQvGknDfws2Z"" Version=""2.0""
  InResponseTo=""_6c3a4f8b9c2d""
  IssueInstant=""2004-01-21T19:00:49Z"">
  <Issuer>https://IdentityProvider.com/SAML</Issuer>
  <ds:Signature xmlns:ds=""http://www.w3.org/2000/09/xmldsig#"">Not parsed...</ds:Signature>
  <samlp:Extensions>Extended data</samlp:Extensions>
  <samlp:Status>
    <samlp:StatusCode
      Value=""urn:oasis:names:tc:SAML:2.0:status:Success""/>
  </samlp:Status>
  <samlp:LogoutRequest ID=""d2b7c388cec36fa7c39c28fd298644a8""
    IssueInstant=""2004-01-21T19:00:49Z""
    Version=""2.0"">
    <Issuer>https://IdentityProvider.com/SAML</Issuer>
    <NameID Format=""urn:oasis:names:tc:SAML:2.0:nameidformat:persistent"">005a06e0-ad82-110d-a556-004005b13a2b</NameID>
    <samlp:SessionIndex>1</samlp:SessionIndex>
  </samlp:LogoutRequest>
</samlp:ArtifactResponse>";

            var xmlDocument = XmlHelpers.FromString(xml);
            var xmlElement  = xmlDocument.DocumentElement;

            var subject = new Saml2ArtifactResponse(xmlElement);

            subject.GetMessage().LocalName.Should().Be("LogoutRequest");
        }
Ejemplo n.º 7
0
        public void XmlHelpers_IsSignedBy_ThrowsOnIncorrectTransformsInSignature()
        {
            // SAML2 Core 5.4.4 states that signatures SHOULD NOT contain other transforms than
            // the enveloped signature or exclusive canonicalization transforms and that a verifier
            // of a signature MAY reject signatures with other transforms. We'll reject them to
            // mitigate the risk of transforms opening up for assertion injections.

            var xml = "<xml ID=\"MyXml\" />";

            var xmlDoc = XmlHelpers.FromString(xml);

            var signedXml = new SignedXml(xmlDoc);

            signedXml.SigningKey = (RSACryptoServiceProvider)SignedXmlHelper.TestCert.PrivateKey;
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

            var reference = new Reference {
                Uri = "#MyXml"
            };

            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            reference.AddTransform(new XmlDsigC14NTransform()); // The allowed transform is XmlDsigExcC14NTransform
            signedXml.AddReference(reference);

            signedXml.ComputeSignature();
            xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signedXml.GetXml(), true));

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("Transform \"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" found in Xml signature SHOULD NOT be used with SAML2.");
        }
Ejemplo n.º 8
0
        public void XmlHelpers_IsSignedBy_ChecksDigestAlgorithmStrength()
        {
            var xml = "<xml ID=\"MyXml\" />";

            var xmlDoc = XmlHelpers.FromString(xml);
            var sx     = new SignedXml(xmlDoc);

            var reference = new Reference
            {
                Uri          = "#MyXml",
                DigestMethod = SignedXml.XmlDsigSHA1Url
            };

            reference.AddTransform(new XmlDsigExcC14NTransform());
            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
            sx.AddReference(reference);
            sx.SigningKey = ((RSACryptoServiceProvider)SignedXmlHelper.TestCert.PrivateKey)
                            .GetSha256EnabledRSACryptoServiceProvider();

            sx.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA256Url;
            sx.ComputeSignature();
            xmlDoc.DocumentElement.AppendChild(sx.GetXml());

            var keyClause = new X509RawDataKeyIdentifierClause(SignedXmlHelper.TestCert);

            xmlDoc.DocumentElement.Invoking(x =>
                                            x.IsSignedByAny(Enumerable.Repeat(keyClause, 1), false, SignedXml.XmlDsigRSASHA256Url))
            .ShouldThrow <InvalidSignatureException>()
            .WithMessage("*digest*weak*");
        }
Ejemplo n.º 9
0
        public void XmlHelpers_IsSignedBy_ThrowsOnMissingReferenceInSignature()
        {
            var signedWithoutReference = @"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" ID=""Saml2Response_Validate_FalseOnMissingReference"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z""><saml2:Issuer>https://idp.example.com</saml2:Issuer><saml2p:Status><saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /></saml2p:Status><Signature xmlns=""http://www.w3.org/2000/09/xmldsig#""><SignedInfo><CanonicalizationMethod Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315"" /><SignatureMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#rsa-sha1"" /></SignedInfo><SignatureValue>tYFIoYmrzmp3H7TXm9IS8DW3buBZIb6sI2ycrn+AOnVcdYnPTJpk3ntHlqQKXNEyXgXZNdqEuFpgI1I0P0TlhM+C3rBJnflkApkxZkak5RwnJzDWTHpsSDjYcm+/XgBy3JVZJuMWb2YPaV8GB6cjBMDrENUEaoKRg+FpzPUZO1EOMcqbocXp5cHie1CkPnD1OtT/cuzMBUMpBGZMxjZwdFpOO7R3CUXh/McxKfoGUQGC3DVpt5T8uGkpj4KqZVPS/qTCRhbPRDjg73BdWbdkFpFWge8G/FgkYxr9LBE1TsrxptppO9xoA5jXwJVZaWndSMvo6TuOjUgqY2w5RTkqhA==</SignatureValue></Signature></saml2p:Response>";

            var xmlDoc = XmlHelpers.FromString(signedWithoutReference);

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("No reference found in Xml signature, it doesn't validate the Xml data.");
        }
Ejemplo n.º 10
0
        public void XmlHelpers_IsSignedBy_ThrowsInformativeMessageOnSha256Signature()
        {
            var xmlSignedWithSha256 = @"<Assertion ID=""Saml2Response_GetClaims_ThrowsInformativeExceptionForSha256"" IssueInstant=""2015-03-13T20:43:07.330Z"" Version=""2.0"" xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""><Issuer>https://idp.example.com</Issuer><Signature xmlns=""http://www.w3.org/2000/09/xmldsig#""><SignedInfo><CanonicalizationMethod Algorithm=""http://www.w3.org/2001/10/xml-exc-c14n#"" /><SignatureMethod Algorithm=""http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"" /><Reference URI=""#Saml2Response_GetClaims_ThrowsInformativeExceptionForSha256""><Transforms><Transform Algorithm=""http://www.w3.org/2000/09/xmldsig#enveloped-signature"" /><Transform Algorithm=""http://www.w3.org/2001/10/xml-exc-c14n#"" /></Transforms><DigestMethod Algorithm=""http://www.w3.org/2001/04/xmlenc#sha256"" /><DigestValue>F+E7u3vqMC07ipvP9AowsMqP7y6CsAC0GeEIxNSwDEI=</DigestValue></Reference></SignedInfo><SignatureValue>GmiXn24Ccnr64TbmDd1/nLM+891z0FtRHSpU8+75uOqbpNK/ZZGrltFf2YZ5u9b9O0HfbFFsZ0i28ocwAZOv2UfxQrCtOGf3ss7Q+t2Zmc6Q/3ES7HIa15I5BbaSdNfpOMlX6N1XXhMprRGy2YWMr5IAIhysFG1A2oHaC3yFiesfUrawN/lXUYuI22Kf4A5bmnIkKijnwX9ewnhRj6569bw+c6q+tVZSHQzI+KMU9KbKN4NsXxAmv6dM1w2qOiX9/CO9LzwEtlhA9yo3sl0uWP8z5GwK9qgOlsF2NdImAQ5f0U4Uv26doFn09W+VExFwNhcXhewQUuPBYBr+XXzdww==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIDIzCCAg+gAwIBAgIQg7mOjTf994NAVxZu4jqXpzAJBgUrDgMCHQUAMCQxIjAgBgNVBAMTGUtlbnRvci5BdXRoU2VydmljZXMuVGVzdHMwHhcNMTMwOTI1MTMzNTQ0WhcNMzkxMjMxMjM1OTU5WjAkMSIwIAYDVQQDExlLZW50b3IuQXV0aFNlcnZpY2VzLlRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVGpfvK9N//MnA5Jo1q2liyPR24406Dp25gv7LB3HK4DWgqsb7xXM6KIV/WVOyCV2g/O1ErBlB+HLhVZ4XUJvbqBbgAJqFO+TZwcCIe8u4nTEXeU660FdtkKClA17sbtMrAGdDfOPwVBHSuavdHeD7jHNI4RUDGKnEW13/0EvnHDilIetwODRxrX/+41R24sJThFbMczByS3OAL2dcIxoAynaGeM90gXsVYow1QhJUy21+cictikb7jW4mW6dvFCBrWIceom9J295DcQIHoxJy5NoZwMir/JV00qs1wDVoN20Ve1DC5ImwcG46XPF7efQ44yLh2j5Yexw+xloA81dwIDAQABo1kwVzBVBgNVHQEETjBMgBAWIahoZhXVUogbAqkS7zwfoSYwJDEiMCAGA1UEAxMZS2VudG9yLkF1dGhTZXJ2aWNlcy5UZXN0c4IQg7mOjTf994NAVxZu4jqXpzAJBgUrDgMCHQUAA4IBAQA2aGzmuKw4AYXWMhrGj5+i8vyAoifUn1QVOFsUukEA77CrqhqqaWFoeagfJp/45vlvrfrEwtF0QcWfmO9w1VvHwm7sk1G/cdYyJ71sU+llDsdPZm7LxQvWZYkK+xELcinQpSwt4ExavS+jLcHoOYHYwIZMBn3U8wZw7Kq29oGnoFQz7HLCEl/G9i3QRyvFITNlWTjoScaqMjHTzq6HCMaRsL09DLcY3KB+cedfpC0/MBlzaxZv0DctTulyaDfM9DCYOyokGN/rQ6qkAR0DDm8fVwknbJY7kURXNGoUetulTb5ow8BvD1gncOaYHSD0kbHZG+bLsUZDFatEr2KW8jbG</X509Certificate></X509Data></KeyInfo></Signature><Subject><NameID>SomeUser</NameID><SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /></Subject><Conditions NotOnOrAfter=""2100-01-01T05:00:00.000Z"" /></Assertion>";

            var xmlDoc = XmlHelpers.FromString(xmlSignedWithSha256);

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("SHA256 signatures require the algorithm to be registered at the process level. Call Kentor.AuthServices.Configuration.Options.GlobalEnableSha256XmlSignatures() on startup to register.");
        }
Ejemplo n.º 11
0
        public void XmlHelpers_IsSignedBy_ThrowsOnWrongCert()
        {
            var xml    = "<xml ID=\"someID\"><content>text</content></xml>";
            var xmlDoc = XmlHelpers.FromString(xml);

            xmlDoc.Sign(SignedXmlHelper.TestCert2, true);

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("The signature verified correctly with the key contained in the signature, but that key is not trusted.");
        }
        public void Saml2RedirectBinding_Unbind_WithoutRelayStateAndSignature()
        {
            var request = new HttpRequestData("GET", new Uri("http://localhost?SAMLRequest=" + ExampleSerializedData));

            var result = Saml2Binding.Get(Saml2BindingType.HttpRedirect).Unbind(request, null);

            var expectedXml = XmlHelpers.FromString(ExampleXmlData).DocumentElement;

            result.RelayState.Should().Be(null);
            result.Data.Should().BeEquivalentTo(expectedXml);
            result.TrustLevel.Should().Be(TrustLevel.None);
        }
Ejemplo n.º 13
0
        public void XmlHelpers_IsSignedByAny_ThrowsOnCertValidationWithRsaKey()
        {
            var xml = "<xml ID=\"MyXml\" />";

            var xmlDoc = XmlHelpers.FromString(xml);

            xmlDoc.Sign(SignedXmlHelper.TestCert, false);

            var signingKeys = Enumerable.Repeat(SignedXmlHelper.TestKey, 1);

            xmlDoc.DocumentElement.Invoking(x => x.IsSignedByAny(signingKeys, true, SignedXml.XmlDsigRSASHA1Url))
            .ShouldThrow <InvalidOperationException>()
            .And.Message.Should().Be("Certificate validation enabled, but the signing key identifier is of type RsaKeyIdentifierClause which cannot be validated as a certificate.");
        }
Ejemplo n.º 14
0
        public void XmlHelpers_IsSignedBy_ThrowsOnTamperedData()
        {
            var xml    = "<xml ID=\"someID\"><content>text</content></xml>";
            var xmlDoc = XmlHelpers.FromString(xml);

            xmlDoc.Sign(SignedXmlHelper.TestCert);

            xmlDoc.DocumentElement["content"].InnerText = "changedText";

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("Signature didn't verify. Have the contents been tampered with?");
        }
Ejemplo n.º 15
0
        public void XmlHelpers_IsSignedBy_ThrowsOnTamperedData_WithSha256Signature()
        {
            Options.GlobalEnableSha256XmlSignatures();

            var xml = @"<Assertion ID=""Saml2Response_GetClaims_FailsSha256WhenChanged"" IssueInstant=""2015-03-13T20:44:00.791Z"" Version=""2.0"" xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""><Issuer>https://idp.example.com</Issuer><Signature xmlns=""http://www.w3.org/2000/09/xmldsig#""><SignedInfo><CanonicalizationMethod Algorithm=""http://www.w3.org/2001/10/xml-exc-c14n#"" /><SignatureMethod Algorithm=""http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"" /><Reference URI=""#Saml2Response_GetClaims_FailsSha256WhenChanged""><Transforms><Transform Algorithm=""http://www.w3.org/2000/09/xmldsig#enveloped-signature"" /><Transform Algorithm=""http://www.w3.org/2001/10/xml-exc-c14n#"" /></Transforms><DigestMethod Algorithm=""http://www.w3.org/2001/04/xmlenc#sha256"" /><DigestValue>BKRyWqweAczLA8fgRcx6zzMDiP0qT0TwqU/X4VgLiXM=</DigestValue></Reference></SignedInfo><SignatureValue>iK8s+MkLlixSSQu5Q/SHRZLhfnj4jlyPLAD6C2n9zmQu4CosZME7mxiNFiWyOE8XRGd+2LJle+NjJrkZFktVb03JaToq7w4Q8GfJ2oUUjNCweoaJ6NzsnwkFoXhyh0dfOixl/Ifa3qDX50/Hv2twF/QXfDs08GZTxZKehKsVDITyVd6nytF8VUb0+nU7UMWPn1XeHM7YNI/1mkVbCRx/ci5ZRxwjAX40xttd4JL6oBnp5oaaMgWpAa2cVb+t/9HhCRThEho1etbPHx/+E9ElL1PhKqKX6nh2GSH1TFJkwEXIPPZKqCs3YDINLBZpLfl626zbV4cGOGyWUAroVsk2uw==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIDIzCCAg+gAwIBAgIQg7mOjTf994NAVxZu4jqXpzAJBgUrDgMCHQUAMCQxIjAgBgNVBAMTGUtlbnRvci5BdXRoU2VydmljZXMuVGVzdHMwHhcNMTMwOTI1MTMzNTQ0WhcNMzkxMjMxMjM1OTU5WjAkMSIwIAYDVQQDExlLZW50b3IuQXV0aFNlcnZpY2VzLlRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVGpfvK9N//MnA5Jo1q2liyPR24406Dp25gv7LB3HK4DWgqsb7xXM6KIV/WVOyCV2g/O1ErBlB+HLhVZ4XUJvbqBbgAJqFO+TZwcCIe8u4nTEXeU660FdtkKClA17sbtMrAGdDfOPwVBHSuavdHeD7jHNI4RUDGKnEW13/0EvnHDilIetwODRxrX/+41R24sJThFbMczByS3OAL2dcIxoAynaGeM90gXsVYow1QhJUy21+cictikb7jW4mW6dvFCBrWIceom9J295DcQIHoxJy5NoZwMir/JV00qs1wDVoN20Ve1DC5ImwcG46XPF7efQ44yLh2j5Yexw+xloA81dwIDAQABo1kwVzBVBgNVHQEETjBMgBAWIahoZhXVUogbAqkS7zwfoSYwJDEiMCAGA1UEAxMZS2VudG9yLkF1dGhTZXJ2aWNlcy5UZXN0c4IQg7mOjTf994NAVxZu4jqXpzAJBgUrDgMCHQUAA4IBAQA2aGzmuKw4AYXWMhrGj5+i8vyAoifUn1QVOFsUukEA77CrqhqqaWFoeagfJp/45vlvrfrEwtF0QcWfmO9w1VvHwm7sk1G/cdYyJ71sU+llDsdPZm7LxQvWZYkK+xELcinQpSwt4ExavS+jLcHoOYHYwIZMBn3U8wZw7Kq29oGnoFQz7HLCEl/G9i3QRyvFITNlWTjoScaqMjHTzq6HCMaRsL09DLcY3KB+cedfpC0/MBlzaxZv0DctTulyaDfM9DCYOyokGN/rQ6qkAR0DDm8fVwknbJY7kURXNGoUetulTb5ow8BvD1gncOaYHSD0kbHZG+bLsUZDFatEr2KW8jbG</X509Certificate></X509Data></KeyInfo></Signature><Subject><NameID>SomeUser</NameID><SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /></Subject><Conditions NotOnOrAfter=""2100-01-01T05:00:00.000Z"" /></Assertion>";

            xml = xml.Replace("SomeUser", "OtherUser");

            var xmlDoc = XmlHelpers.FromString(xml);

            xmlDoc.DocumentElement.Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .WithMessage("Signature didn't verify. Have the contents been tampered with?");
        }
Ejemplo n.º 16
0
        public void XmlHelpers_PrettyPrint()
        {
            var xmlDoc = new XmlDocument();

            xmlDoc.LoadXml("<a><b>c</b></a>");

            var result = xmlDoc.DocumentElement.PrettyPrint();

            var parsed = XmlHelpers.FromString(result);

            var expected = "<a>\r\n  <b>c</b>\r\n</a>";

            parsed.OuterXml.Should().Be(expected);
            // Don't change semantics.
            parsed.DocumentElement.Should().BeEquivalentTo(xmlDoc.DocumentElement);
        }
Ejemplo n.º 17
0
        public void LogoutCommand_Run_HandlesLogoutResponse_InPost()
        {
            var relayState = "TestState";
            var response   = new Saml2LogoutResponse(Saml2StatusCode.Success)
            {
                DestinationUrl     = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer             = new EntityId("https://idp.example.com"),
                InResponseTo       = new Saml2Id(),
                SigningCertificate = SignedXmlHelper.TestCert
            };

            var xml = XmlHelpers.FromString(response.ToXml());

            xml.Sign(SignedXmlHelper.TestCert);

            var responseData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair <string, string[]>[]
            {
                new KeyValuePair <string, string[]>("SAMLResponse", new[] { responseData }),
                new KeyValuePair <string, string[]>("RelayState", new[] { relayState })
            },
                Enumerable.Empty <KeyValuePair <string, string> >(),
                null);

            httpRequest.StoredRequestState = new StoredRequestState(null, new Uri("http://loggedout.example.com"), null, null);

            var options = StubFactory.CreateOptions();

            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                         .Run(httpRequest, options);

            var expected = new CommandResult
            {
                Location        = new Uri("http://loggedout.example.com"),
                HttpStatusCode  = HttpStatusCode.SeeOther,
                ClearCookieName = "Kentor." + relayState
            };

            actual.ShouldBeEquivalentTo(expected);
        }
Ejemplo n.º 18
0
        public void XmlHelpers_IsSignedBy_HandlesMultipleSignaturesInSameDocument()
        {
            var content1 = SignedXmlHelper.SignXml("<content ID=\"c1\" />");
            var content2 = SignedXmlHelper.SignXml("<content ID=\"c2\" />");

            var xml =
                $@"<xml>
    {content1}
    {content2}
</xml>";

            var xmlDoc = XmlHelpers.FromString(xml);

            ((XmlElement)xmlDoc.SelectSingleNode("//*[@ID=\"c1\"]")).IsSignedBy(SignedXmlHelper.TestCert)
            .Should().BeTrue("first content is correclty signed");

            ((XmlElement)xmlDoc.SelectSingleNode("//*[@ID=\"c2\"]")).IsSignedBy(SignedXmlHelper.TestCert)
            .Should().BeTrue("second content is correclty signed");
        }
        public void Saml2LogoutRequest_FromXml_OnlyRequiredData()
        {
            var xmlData =
                @"<saml2p:LogoutRequest xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
 ID=""d2b7c388cec36fa7c39c28fd298644a8"" Version=""2.0"" IssueInstant=""2004-01-21T19:00:49Z"">
 <saml2:NameID>005a06e0-ad82-110d-a556-004005b13a2b</saml2:NameID>
</saml2p:LogoutRequest>";

            var xmlDoc = XmlHelpers.FromString(xmlData);

            var subject = Saml2LogoutRequest.FromXml(xmlDoc.DocumentElement);

            var expected = new Saml2LogoutRequest(new Saml2Id("d2b7c388cec36fa7c39c28fd298644a8"))
            {
                NameId = new Saml2NameIdentifier("005a06e0-ad82-110d-a556-004005b13a2b"),
            };

            subject.ShouldBeEquivalentTo(expected);
        }
Ejemplo n.º 20
0
        public void Saml2PostBinding_Unbind_Request()
        {
            var requestData = Convert.ToBase64String(Encoding.UTF8.GetBytes("<data/>"));

            var request = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair <string, string[]>[]
            {
                new KeyValuePair <string, string[]>("SAMLRequest", new[] { requestData })
            },
                null,
                null);

            var actual = Saml2Binding.Get(request).Unbind(request, StubFactory.CreateOptions());

            actual.Data.Should().BeEquivalentTo(XmlHelpers.FromString("<data/>").DocumentElement);
            actual.RelayState.Should().BeNull();
            actual.TrustLevel.Should().Be(TrustLevel.None);
        }
Ejemplo n.º 21
0
        public void LogoutCommand_Run_DetectsSignatureInLogoutRequestReceivedThroughPostBinding()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl     = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer             = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId             = new Saml2NameIdentifier("NameId"),
                SessionIndex       = "SessionID",
                SigningAlgorithm   = SignedXml.XmlDsigRSASHA256Url
            };

            var xml = XmlHelpers.FromString(request.ToXml());

            xml.Sign(SignedXmlHelper.TestCert);

            var requestData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair <string, string[]>[]
            {
                new KeyValuePair <string, string[]>("SAMLRequest", new[] { requestData })
            },
                null,
                null);

            var options = StubFactory.CreateOptions();

            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                         .Run(httpRequest, options);

            HttpUtility.ParseQueryString(actual.Location.Query)
            .Keys.Should().Contain("SAMLResponse", "if the request was properly detected a response should be generated");
        }
        public void Saml2LogoutRequest_ToXml()
        {
            var subject = new Saml2LogoutRequest()
            {
                DestinationUrl = new Uri("http://idp.example.com/logout"),
                Issuer         = new EntityId("http://sp.example.com/"),
                NameId         = new Saml2NameIdentifier("005a06e0-ad82-110d-a556-004005b13a2b")
                {
                    Format          = new Uri(NameIdFormat.Persistent.GetUri().AbsoluteUri),
                    NameQualifier   = "qualifier",
                    SPNameQualifier = "spQualifier",
                    SPProvidedId    = "spId"
                },
                SessionIndex = "SessionId"
            };

            var actual = XmlHelpers.FromString(subject.ToXml());

            var expected = XmlHelpers.FromString(
                @"<saml2p:LogoutRequest xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
 ID=""d2b7c388cec36fa7c39c28fd298644a8"" Version=""2.0"" IssueInstant=""2004-01-21T19:00:49Z""
 Destination=""http://idp.example.com/logout"">
 <saml2:Issuer>http://sp.example.com/</saml2:Issuer>
 <saml2:NameID Format=""urn:oasis:names:tc:SAML:2.0:nameid-format:persistent""
    NameQualifier = ""qualifier""
    SPNameQualifier = ""spQualifier""
    SPProvidedID = ""spId"">005a06e0-ad82-110d-a556-004005b13a2b</saml2:NameID>
 <saml2p:SessionIndex>SessionId</saml2p:SessionIndex>
</saml2p:LogoutRequest>");

            // Set generated expected values to the actual.
            expected.DocumentElement.Attributes["ID"].Value =
                actual.DocumentElement.Attributes["ID"].Value;
            expected.DocumentElement.Attributes["IssueInstant"].Value =
                actual.DocumentElement.Attributes["IssueInstant"].Value;

            actual.Should().BeEquivalentTo(expected);
        }
Ejemplo n.º 23
0
        public void XmlHelpers_IsSignedBy_TrowsOnSignatureWrapping()
        {
            var xml = "<xml ID=\"someID\"><content ID=\"content1\">text</content>"
                      + "<injected>other text</injected></xml>";
            var xmlDoc = XmlHelpers.FromString(xml);

            xmlDoc.DocumentElement["content"].Sign(SignedXmlHelper.TestCert, false);

            // An XML wrapping attack is created by taking a legitimate signature
            // and putting it in another element. If the reference of the signature
            // is not properly checked, the element containing the signature
            // is incorrectly trusted.
            var signatureNode = xmlDoc.DocumentElement["content"]["Signature", SignedXml.XmlDsigNamespaceUrl];

            xmlDoc.DocumentElement["content"].RemoveChild(signatureNode);
            xmlDoc.DocumentElement["injected"].AppendChild(signatureNode);

            xmlDoc.DocumentElement["injected"].Invoking(
                x => x.IsSignedBy(SignedXmlHelper.TestCert))
            .ShouldThrow <InvalidSignatureException>()
            .And.Message.Should().Be("Incorrect reference on Xml signature. The reference must be to the root element of the element containing the signature.");
        }
Ejemplo n.º 24
0
        public void LogoutCommand_Run_ChecksSignatureAlgorithmStrength()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl     = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer             = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                SigningAlgorithm   = SignedXml.XmlDsigRSASHA256Url, // Ignored
                NameId             = new Saml2NameIdentifier("NameId"),
                SessionIndex       = "SessionID"
            };

            var xml = XmlHelpers.FromString(request.ToXml());

            xml.Sign(SignedXmlHelper.TestCert);

            var requestData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair <string, string[]>[]
            {
                new KeyValuePair <string, string[]>("SAMLRequest", new[] { requestData })
            },
                null,
                null);

            var options = StubFactory.CreateOptions();

            options.SPOptions.MinIncomingSigningAlgorithm = SignedXml.XmlDsigRSASHA384Url;
            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
            .Invoking(c => c.Run(httpRequest, options))
            .ShouldThrow <InvalidSignatureException>()
            .WithMessage("*weak*");
        }
Ejemplo n.º 25
0
        public void LogoutCommand_Run_ThrowsOnSignatureInLogoutRequestReceivedThroughPostBindingIfCertificateIsntValid_WhenCertificateValidationIsConfigured()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl     = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer             = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId             = new Saml2NameIdentifier("NameId"),
                SessionIndex       = "SessionID",
            };

            var xml = XmlHelpers.FromString(request.ToXml());

            xml.Sign(SignedXmlHelper.TestCert);

            var requestData = Convert.ToBase64String(Encoding.UTF8.GetBytes(xml.OuterXml));

            var httpRequest = new HttpRequestData(
                "POST",
                new Uri("http://something"),
                "/path",
                new KeyValuePair <string, string[]>[]
            {
                new KeyValuePair <string, string[]>("SAMLRequest", new[] { requestData })
            },
                null,
                null);

            var options = StubFactory.CreateOptions();

            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);
            options.SPOptions.ValidateCertificates = true;

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                         .Invoking(c => c.Run(httpRequest, options))
                         .ShouldThrow <InvalidSignatureException>()
                         .WithMessage("The signature was valid, but the verification of the certificate failed. Is it expired or revoked? Are you sure you really want to enable ValidateCertificates (it's normally not needed)?");
        }
        public void Saml2LogoutResponse_AppendTo_Minimal()
        {
            var subject = new Saml2LogoutResponse(Saml2StatusCode.Success);

            var expectedXml =
                $@"<samlp:LogoutResponse 
                        xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
                        xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
                        ID=""{subject.Id}""
                        Version=""2.0""
                        IssueInstant=""{subject.IssueInstant.ToSaml2DateTimeString()}"">
                        <samlp:Status>
                            <samlp:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success""/>
                        </samlp:Status>
                    </samlp:LogoutResponse>";

            var expectedNode = XmlHelpers.FromString(expectedXml).DocumentElement;

            var xmlDoc = new XmlDocument();

            subject.AppendTo(xmlDoc);

            xmlDoc.DocumentElement.Should().BeEquivalentTo(expectedNode, "XML should be a valid LogoutResponse");
        }
Ejemplo n.º 27
0
        public void LogoutCommand_Run_HandlesLogoutRequest_ReceivedThroughRedirectBinding()
        {
            var request = new Saml2LogoutRequest()
            {
                DestinationUrl     = new Uri("http://sp.example.com/path/AuthServices/logout"),
                Issuer             = new EntityId("https://idp.example.com"),
                SigningCertificate = SignedXmlHelper.TestCert,
                NameId             = new Saml2NameIdentifier("NameId"),
                SessionIndex       = "SessionID",
            };

            var bindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                             .Bind(request);

            var httpRequest = new HttpRequestData("GET", bindResult.Location);

            var options = StubFactory.CreateOptions();

            options.SPOptions.ServiceCertificates.Add(SignedXmlHelper.TestCert);

            CommandResult notifiedCommandResult = null;

            options.Notifications.LogoutCommandResultCreated = cr =>
            {
                notifiedCommandResult = cr;
            };

            // We're using unbind to verify the created message and UnBind
            // expects the issuer to be a known Idp for signature validation.
            // Add a dummy with the right issuer name and key.
            var dummyIdp = new IdentityProvider(options.SPOptions.EntityId, options.SPOptions);

            dummyIdp.SigningKeys.AddConfiguredKey(SignedXmlHelper.TestCert);
            options.IdentityProviders.Add(dummyIdp);

            var actual = CommandFactory.GetCommand(CommandFactory.LogoutCommandName)
                         .Run(httpRequest, options);

            var expected = new CommandResult()
            {
                HttpStatusCode        = HttpStatusCode.SeeOther,
                TerminateLocalSession = true
                                        // Deliberately not comparing Location
            };

            HttpUtility.ParseQueryString(actual.Location.Query)["Signature"]
            .Should().NotBeNull("LogoutResponse should be signed");

            actual.ShouldBeEquivalentTo(expected, opt => opt.Excluding(cr => cr.Location));
            actual.Should().BeSameAs(notifiedCommandResult);

            var actualUnbindResult = Saml2Binding.Get(Saml2BindingType.HttpRedirect)
                                     .Unbind(new HttpRequestData("GET", actual.Location), options);

            var actualMessage = actualUnbindResult.Data;

            var expectedMessage = XmlHelpers.FromString(
                $@"<samlp:LogoutResponse xmlns:samlp=""urn:oasis:names:tc:SAML:2.0:protocol""
                    xmlns=""urn:oasis:names:tc:SAML:2.0:assertion""
                    Destination=""https://idp.example.com/logout""
                    Version=""2.0"">
                    <Issuer>{options.SPOptions.EntityId.Id}</Issuer>
                    <samlp:Status>
                        <samlp:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success""/>
                    </samlp:Status>
                </samlp:LogoutResponse>").DocumentElement;

            // Set generated attributes to actual values.
            expectedMessage.SetAttribute("ID", actualMessage.GetAttribute("ID"));
            expectedMessage.SetAttribute("IssueInstant", actualMessage.GetAttribute("IssueInstant"));
            expectedMessage.SetAttribute("InResponseTo", request.Id.Value);

            actualMessage.Should().BeEquivalentTo(expectedMessage);

            actualUnbindResult.RelayState.Should().Be(request.RelayState);
            actualUnbindResult.TrustLevel.Should().Be(TrustLevel.Signature);
        }
Ejemplo n.º 28
0
        public void MetadataCommand_Run_CompleteMetadata()
        {
            var options = StubFactory.CreateOptions();

            options.SPOptions.DiscoveryServiceUrl = new Uri("http://ds.example.com");
            options.SPOptions.AuthenticateRequestSigningBehavior = SigningBehavior.Always;
            options.SPOptions.SigningAlgorithm = SignedXml.XmlDsigRSASHA384Url;
            options.SPOptions.ServiceCertificates.Add(new ServiceCertificate()
            {
                Certificate             = SignedXmlHelper.TestCertSignOnly,
                Use                     = CertificateUse.Signing,
                MetadataPublishOverride = MetadataPublishOverrideType.PublishUnspecified
            });

            var subject = new MetadataCommand().Run(request, options);

            var payloadXml = XmlHelpers.FromString(subject.Content);

            // Validate signature, location of it  and then drop it. It contains
            // a reference to the ID which makes it unsuitable for string matching.
            payloadXml.DocumentElement.IsSignedBy(SignedXmlHelper.TestCertSignOnly).Should().BeTrue();
            payloadXml.DocumentElement.FirstChild.LocalName.Should().Be("Signature");
            payloadXml.DocumentElement.FirstChild["KeyInfo"].Should().NotBeNull();
            payloadXml.DocumentElement.FirstChild["SignedInfo"]["SignatureMethod"].GetAttribute("Algorithm")
            .Should().Be(SignedXml.XmlDsigRSASHA384Url);
            payloadXml.DocumentElement.RemoveChild("Signature", SignedXml.XmlDsigNamespaceUrl);

            // Ignore the ID attribute, it is just filled with a GUID that can't be easily tested.
            payloadXml.DocumentElement.Attributes.Remove("ID");

            // Test and then drop validUntil, can't be text compared.
            DateTime.Parse(payloadXml.DocumentElement.Attributes["validUntil"].Value)
            .Should().BeCloseTo(DateTime.UtcNow.AddDays(24).ToLocalTime(), 2000);
            payloadXml.DocumentElement.Attributes.Remove("validUntil");

            var expectedXml =
                "<EntityDescriptor entityID=\"https://github.com/KentorIT/authservices\" cacheDuration=\"PT42S\" xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\">"
                + "<SPSSODescriptor AuthnRequestsSigned=\"true\" WantAssertionsSigned=\"true\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">"
                + "<Extensions>"
                + "<DiscoveryResponse Binding=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" Location=\"http://localhost/AuthServices/SignIn\" index=\"0\" isDefault=\"true\" xmlns=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" />"
                + "</Extensions>"
                + "<KeyDescriptor><KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><X509Data><X509Certificate>MIIDIzCCAg+gAwIBAgIQg7mOjTf994NAVxZu4jqXpzAJBgUrDgMCHQUAMCQxIjAgBgNVBAMTGUtlbnRvci5BdXRoU2VydmljZXMuVGVzdHMwHhcNMTMwOTI1MTMzNTQ0WhcNMzkxMjMxMjM1OTU5WjAkMSIwIAYDVQQDExlLZW50b3IuQXV0aFNlcnZpY2VzLlRlc3RzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVGpfvK9N//MnA5Jo1q2liyPR24406Dp25gv7LB3HK4DWgqsb7xXM6KIV/WVOyCV2g/O1ErBlB+HLhVZ4XUJvbqBbgAJqFO+TZwcCIe8u4nTEXeU660FdtkKClA17sbtMrAGdDfOPwVBHSuavdHeD7jHNI4RUDGKnEW13/0EvnHDilIetwODRxrX/+41R24sJThFbMczByS3OAL2dcIxoAynaGeM90gXsVYow1QhJUy21+cictikb7jW4mW6dvFCBrWIceom9J295DcQIHoxJy5NoZwMir/JV00qs1wDVoN20Ve1DC5ImwcG46XPF7efQ44yLh2j5Yexw+xloA81dwIDAQABo1kwVzBVBgNVHQEETjBMgBAWIahoZhXVUogbAqkS7zwfoSYwJDEiMCAGA1UEAxMZS2VudG9yLkF1dGhTZXJ2aWNlcy5UZXN0c4IQg7mOjTf994NAVxZu4jqXpzAJBgUrDgMCHQUAA4IBAQA2aGzmuKw4AYXWMhrGj5+i8vyAoifUn1QVOFsUukEA77CrqhqqaWFoeagfJp/45vlvrfrEwtF0QcWfmO9w1VvHwm7sk1G/cdYyJ71sU+llDsdPZm7LxQvWZYkK+xELcinQpSwt4ExavS+jLcHoOYHYwIZMBn3U8wZw7Kq29oGnoFQz7HLCEl/G9i3QRyvFITNlWTjoScaqMjHTzq6HCMaRsL09DLcY3KB+cedfpC0/MBlzaxZv0DctTulyaDfM9DCYOyokGN/rQ6qkAR0DDm8fVwknbJY7kURXNGoUetulTb5ow8BvD1gncOaYHSD0kbHZG+bLsUZDFatEr2KW8jbG</X509Certificate></X509Data></KeyInfo></KeyDescriptor>"
                + "<SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"http://localhost/AuthServices/Logout\" />"
                + "<SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost/AuthServices/Logout\" />"
                + "<AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost/AuthServices/Acs\" index=\"0\" isDefault=\"true\" />"
                + "<AssertionConsumerService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\" Location=\"http://localhost/AuthServices/Acs\" index=\"1\" isDefault=\"false\" />"
                + "<AttributeConsumingService index=\"0\" isDefault=\"true\">"
                + "<ServiceName xml:lang=\"en\">attributeServiceName</ServiceName>"
                + "<RequestedAttribute Name=\"urn:attributeName\" isRequired=\"true\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"friendlyName\">"
                + "<saml2:AttributeValue>value1</saml2:AttributeValue>"
                + "<saml2:AttributeValue>value2</saml2:AttributeValue>"
                + "</RequestedAttribute>"
                + "<RequestedAttribute Name=\"someName\" isRequired=\"false\" />"
                + "</AttributeConsumingService>"
                + "</SPSSODescriptor>"
                + "<Organization>"
                + "<OrganizationName xml:lang=\"\">Kentor.AuthServices</OrganizationName>"
                + "<OrganizationDisplayName xml:lang=\"\">Kentor AuthServices</OrganizationDisplayName>"
                + "<OrganizationURL xml:lang=\"\">http://github.com/KentorIT/authservices</OrganizationURL>"
                + "</Organization>"
                + "<ContactPerson contactType=\"support\">"
                + "<Company>Kentor</Company>"
                + "<GivenName>Anders</GivenName>"
                + "<SurName>Abel</SurName>"
                + "<EmailAddress>[email protected]</EmailAddress>"
                + "<EmailAddress>[email protected]</EmailAddress>"
                + "<TelephoneNumber>+46 8 587 650 00</TelephoneNumber>"
                + "<TelephoneNumber>+46 708 96 50 63</TelephoneNumber>"
                + "</ContactPerson>"
                + "<ContactPerson contactType=\"technical\" />"
                + "</EntityDescriptor>";

            payloadXml.Should().BeEquivalentTo(XmlHelpers.FromString(expectedXml));
            subject.ContentType.Should().Be("application/samlmetadata+xml");
        }