public void Saml2Response_Validate_FalseOnReplyFromWrongIdp() { // A valid response is received, but it is not from the idp that we // did send the AuthnRequest to. var idp = IdentityProvider.ConfiguredIdentityProviders.First().Value; var request = idp.CreateAuthenticateRequest(); var responseXML = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_Validate_TrueOnCorrectInResponseTo"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" InResponseTo = """ + request.Id + @"""> <saml2p:Issuer>https://idp.anotheridp.com</saml2p:Issuer> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; responseXML = SignedXmlHelper.SignXml(responseXML); var response = Saml2Response.Read(responseXML); response.Validate(SignedXmlHelper.TestCert).Should().BeFalse(); }
public void Saml2Response_Validate_TrueOnMissingInResponseTo_IfAllowed() { KentorAuthServicesSection.Current.IdentityProviders.First().AllowConfigEdit(true); KentorAuthServicesSection.Current.IdentityProviders.First().AllowUnsolicitedAuthnResponse = true; KentorAuthServicesSection.Current.IdentityProviders.First().AllowConfigEdit(false); var idp = IdentityProvider.ConfiguredIdentityProviders.First().Value; var request = idp.CreateAuthenticateRequest(); var responseXML = @"<?xml version=""1.0"" encoding=""UTF-8""?> <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_TrueOnCorrectInResponseTo"" 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> </saml2p:Response>"; responseXML = SignedXmlHelper.SignXml(responseXML); var response = Saml2Response.Read(responseXML); response.Validate(SignedXmlHelper.TestCert).Should().BeTrue(); }
public void Saml2Response_GetClaims_ThrowsOnResponseNotValid() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_GetClaims_ThrowsOnResponseNotValid"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" Issuer = ""https://some.issuer.example.com""> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" /> </saml2p:Status> <saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_GetClaims_ThrowsOnResponseNotValid_Assertion"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> </saml2:Assertion> </saml2p:Response>"; response = SignedXmlHelper.SignXml(response); response = response.Replace("2013-09-25", "2013-09-26"); var r = Saml2Response.Read(response); r.Validate(SignedXmlHelper.TestCert); Action a = () => r.GetClaims(); a.ShouldThrow <InvalidOperationException>() .WithMessage("The Saml2Response didn't pass validation"); }
public void Saml2Response_GetClaims_ThrowsOnNotValidated() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" ID = ""Saml2Response_GetClaims_ThrowsOnNotValidated"" 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:Success"" /> </saml2p:Status> <saml2:Assertion Version=""2.0"" ID=""Saml2Response_GetClaims_ThrowsOnNotValidated_Assertion"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> </saml2:Assertion> </saml2p:Response>"; Action a = () => Saml2Response.Read(response).GetClaims(); a.ShouldThrow <InvalidOperationException>() .WithMessage("The Saml2Response must be validated first."); }
public void Saml2Response_GetClaims_ThrowsOnExpired() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_GetClaims_ThrowsOnExpired"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" Issuer = ""https://some.issuer.example.com""> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Success"" /> </saml2p:Status> <saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_GetClaims_ThrowsOnExpired_Assertion"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2013-06-30T08:00:00Z"" /> </saml2:Assertion> </saml2p:Response>"; response = SignedXmlHelper.SignXml(response); var r = Saml2Response.Read(response); r.Validate(SignedXmlHelper.TestCert); Action a = () => r.GetClaims(); a.ShouldThrow <SecurityTokenExpiredException>(); }
public void Saml2Response_Read_ThrowsWrongRootNodeName() { Action a = () => Saml2Response.Read("<saml2p:NotResponse xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\" />"); a.ShouldThrow <XmlException>() .WithMessage("Expected a SAML2 assertion document"); }
public void Saml2Response_Read_ThrowsOnNonXml() { Action a = () => Saml2Response.Read("not xml"); a.ShouldThrow <XmlException>() .WithMessage("Data at the root level is invalid. Line 1, position 1."); }
public void Saml2Response_Read_BasicParams() { string response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_Read_BasicParams"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" InResponseTo = ""InResponseToId"" Destination=""http://destination.example.com""> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; var expected = new { Id = new Saml2Id("Saml2Response_Read_BasicParams"), IssueInstant = new DateTime(2013, 01, 01, 0, 0, 0, DateTimeKind.Utc), Status = Saml2StatusCode.Requester, Issuer = (string)null, DestinationUri = new Uri("http://destination.example.com"), MessageName = "SAMLResponse", InResponseTo = new Saml2Id("InResponseToId"), }; Saml2Response.Read(response).ShouldBeEquivalentTo(expected, opt => opt.Excluding(s => s.XmlDocument)); }
public void Saml2Response_Validate_FalseOnAssertionInjectionWithAssertionSignature() { var response = @"<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_FalseOnAssertionInjectionWithAssertionSignature"" 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:Success"" /> </saml2p:Status> {0} {1} </saml2p:Response>"; var assertion1 = @"<saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_Validate_FalseOnAssertionInjectionWithAssertionSignature_Assertion1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion>"; var assertionToInject = @"<saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_Validate_FalseOnAssertionInjectionWithAssertionSignature_Assertion2"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser2</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion>"; var signedAssertion1 = SignedXmlHelper.SignXml(assertion1); var signedAssertion1Doc = new XmlDocument { PreserveWhitespace = true }; signedAssertion1Doc.LoadXml(signedAssertion1); var signatureToCopy = signedAssertion1Doc.DocumentElement["Signature", SignedXml.XmlDsigNamespaceUrl]; var assertionToInjectDoc = new XmlDocument { PreserveWhitespace = true }; assertionToInjectDoc.LoadXml(assertionToInject); assertionToInjectDoc.DocumentElement.AppendChild(assertionToInjectDoc.ImportNode(signatureToCopy, true)); var signedAssertionToInject = assertionToInjectDoc.OuterXml; var signedResponse = string.Format(response, signedAssertion1, signedAssertionToInject); Saml2Response.Read(signedResponse).Validate(SignedXmlHelper.TestCert).Should().BeFalse(); }
public void Saml2Response_Validate_FalseOnTamperedAssertionWithMessageSignature() { var response = @"<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_FalseOnTamperedAssertionWithMessageSignature"" 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:Success"" /> </saml2p:Status> <saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_Validate_FalseOnTamperedAssertionWithMessageSignature_Assertion1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> </saml2p:Response>"; var signedResponse = SignedXmlHelper.SignXml(response).Replace("SomeUser", "SomeOtherUser"); Saml2Response.Read(signedResponse).Validate(SignedXmlHelper.TestCert).Should().BeFalse(); }
public void Saml2Response_Read_ThrowsWrongRootNamespace() { Action a = () => Saml2Response.Read("<saml2p:Response xmlns:saml2p=\"something\" /> "); a.ShouldThrow <XmlException>() .WithMessage("Expected a SAML2 assertion document"); }
public void Saml2Response_Validate_TrueOnCorrectSignedSingleAssertionInResponseMessage() { var response = @"<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_TrueOnCorrectSignedSingleAssertionInResponseMessage"" 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:Success"" /> </saml2p:Status> {0} </saml2p:Response>"; var assertion = @"<saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_Validate_TrueOnCorrectSignedSingleAssertionInResponseMessagee_Assertion1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion>"; var signedAssertion = SignedXmlHelper.SignXml(assertion); var signedResponse = string.Format(response, signedAssertion); Saml2Response.Read(signedResponse).Validate(SignedXmlHelper.TestCert).Should().BeTrue(); }
public void Saml2Response_Validate_FalseOnMissingSignatureInResponseAndAnyAssertion() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" ID = ""Saml2Response_Validates_FalseOnMissingSignatureInResponseAndAnyAssertion"" 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:Success"" /> </saml2p:Status> <saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_Validates_FalseOnMissingSignatureInResponseAndAnyAssertion_Assertion1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> <saml2:Assertion xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" Version=""2.0"" ID=""Saml2Response_Validates_FalseOnMissingSignatureInResponseAndAnyAssertion_Assertion2"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> </saml2p:Response>"; Saml2Response.Read(response).Validate(null).Should().BeFalse(); }
public void Saml2Response_Validate_FalseOnReplayedInResponseTo() { var idp = IdentityProvider.ConfiguredIdentityProviders.First().Value; var request = idp.CreateAuthenticateRequest(); var responseXML = @"<?xml version=""1.0"" encoding=""UTF-8""?> <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_TrueOnCorrectInResponseTo"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" InResponseTo = """ + request.Id + @"""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; responseXML = SignedXmlHelper.SignXml(responseXML); var response = Saml2Response.Read(responseXML); response.Validate(SignedXmlHelper.TestCert).Should().BeTrue(); response = Saml2Response.Read(responseXML); response.Validate(SignedXmlHelper.TestCert).Should().BeFalse(); }
public void Saml2Response_ToXml() { string response = @"<?xml version=""1.0"" encoding=""UTF-8""?><saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID=""Saml2Response_ToXml"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z""><saml2p:Status><saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /></saml2p:Status></saml2p:Response>"; var subject = Saml2Response.Read(response).ToXml(); subject.Should().Be(response); }
public void Saml2Response_Read_ThrowsOnWrongVersion() { Action a = () => Saml2Response.Read("<saml2p:Response xmlns:saml2p=\"" + Saml2Namespaces.Saml2P + "\" Version=\"wrong\" />"); a.ShouldThrow <XmlException>() .WithMessage("Wrong or unsupported SAML2 version"); }
public void Saml2Response_Validate_FalseOnMissingSignature() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_Validates_FalseOnMissingSignature"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" Issuer = ""https://some.issuer.example.com""> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; Saml2Response.Read(response).Validate(null).Should().BeFalse(); }
public void Saml2Response_GetClaims_CreateIdentities() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion"" ID = ""Saml2Response_GetClaims_CreateIdentities"" 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:Success"" /> </saml2p:Status> <saml2:Assertion Version=""2.0"" ID=""Saml2Response_GetClaims_CreateIdentities1"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> <saml2:Assertion Version=""2.0"" ID=""Saml2Response_GetClaims_CreateIdentities2"" IssueInstant=""2013-09-25T00:00:00Z""> <saml2:Issuer>https://idp.example.com</saml2:Issuer> <saml2:Subject> <saml2:NameID>SomeOtherUser</saml2:NameID> <saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" /> </saml2:Subject> <saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" /> </saml2:Assertion> </saml2p:Response>"; var c1 = new ClaimsIdentity("Federation"); c1.AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeUser", null, "https://idp.example.com")); var c2 = new ClaimsIdentity("Federation"); c2.AddClaim(new Claim(ClaimTypes.NameIdentifier, "SomeOtherUser", null, "https://idp.example.com")); var expected = new ClaimsIdentity[] { c1, c2 }; var r = Saml2Response.Read(SignedXmlHelper.SignXml(response)); r.Validate(SignedXmlHelper.TestCert); r.GetClaims().ShouldBeEquivalentTo(expected, opt => opt.IgnoringCyclicReferences()); }
public CommandResult Run(HttpRequestData request, IOptions options) { if (request == null) { throw new ArgumentNullException("request"); } if (options == null) { throw new ArgumentNullException("options"); } var binding = Saml2Binding.Get(request); if (binding != null) { string unpackedPayload = null; try { unpackedPayload = binding.Unbind(request); var samlResponse = Saml2Response.Read(unpackedPayload); return(ProcessResponse(options, samlResponse)); } catch (FormatException ex) { throw new BadFormatSamlResponseException( "The SAML Response did not contain valid BASE64 encoded data.", ex); } catch (XmlException ex) { var newEx = new BadFormatSamlResponseException( "The SAML response contains incorrect XML", ex); // Add the payload to the exception newEx.Data.Add("Saml2Response", unpackedPayload); throw newEx; } catch (Exception ex) { // Add the payload to the existing exception ex.Data.Add("Saml2Response", unpackedPayload); throw; } } throw new NoSamlResponseFoundException(); }
public void Saml2Response_Read_Issuer() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Respons_Read_Issuer"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z""> <saml2:Issuer xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""> https://some.issuer.example.com </saml2:Issuer> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; Saml2Response.Read(response).Issuer.Should().Be("https://some.issuer.example.com"); }
public void Saml2Response_Validate_TrueOnCorrectMessage() { var response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_Validate_TrueOnCorrectMessage"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"" Issuer = ""https://some.issuer.example.com""> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; var signedResponse = SignedXmlHelper.SignXml(response); Saml2Response.Read(signedResponse).Validate(SignedXmlHelper.TestCert).Should().BeTrue(); }
public void Saml2Response_Validate_FalseOnTamperedMessage() { var response = @"<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_FalseOnTamperedMessage"" 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:Success"" /> </saml2p:Status> </saml2p:Response>"; var signedResponse = SignedXmlHelper.SignXml(response); signedResponse = signedResponse.Replace("2013-01-01", "2013-01-02"); Saml2Response.Read(signedResponse).Validate(SignedXmlHelper.TestCert).Should().BeFalse(); }
public void Saml2Response_Validate_ReturnsExistingResultOnSecondValidateCall() { var response = @"<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_TrueOnCorrectSignedResponseMessage"" 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> </saml2p:Response>"; var signedResponse = SignedXmlHelper.SignXml(response); var samlResponse = Saml2Response.Read(signedResponse); samlResponse.Validate(SignedXmlHelper.TestCert).Should().BeTrue(); samlResponse.Validate(SignedXmlHelper.TestCert).Should().BeTrue(); }
public void Saml2Response_Read_BasicParams() { string response = @"<?xml version=""1.0"" encoding=""UTF-8""?> <saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol"" ID = ""Saml2Response_Read_BasicParams"" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z""> <saml2p:Status> <saml2p:StatusCode Value=""urn:oasis:names:tc:SAML:2.0:status:Requester"" /> </saml2p:Status> </saml2p:Response>"; var expected = new { Id = "Saml2Response_Read_BasicParams", IssueInstant = new DateTime(2013, 01, 01, 0, 0, 0, DateTimeKind.Utc), Status = Saml2StatusCode.Requester, Issuer = (string)null }; Saml2Response.Read(response).ShouldBeEquivalentTo(expected); }
public static Saml2Response GetSamlToken(string tokenAsBase64String) { var xml = Encoding.UTF8.GetString(Convert.FromBase64String(tokenAsBase64String)); var token = Saml2Response.Read(xml); IdentityProvider identityProvider; if (!samlOptions.IdentityProviders.TryGetValue(token.Issuer, out identityProvider)) { throw new HttpException(401, "Unkown identity provider"); // Probably not the right thing to do... } Debug.WriteLine(token.Issuer.Id); // token.Validate has been made private in the latest AuthServices release so I can't validate the response // It will eventually be validated when I do the CreateClaims thing but would be nice to do it explicitly //if (!token.Validate(samlOptions)) //{ // throw new HttpException(401, "Invalid SAML token signature"); //} return(token); }
public CommandResult Run(HttpRequestData request, IOptions options) { if (request == null) { throw new ArgumentNullException("request"); } if (options == null) { throw new ArgumentNullException("options"); } var binding = Saml2Binding.Get(request); if (binding != null) { try { var samlResponse = Saml2Response.Read(binding.Unbind(request)); return(ProcessResponse(options, samlResponse)); } catch (FormatException ex) { throw new BadFormatSamlResponseException( "The SAML Response did not contain valid BASE64 encoded data.", ex); } catch (XmlException ex) { throw new BadFormatSamlResponseException( "The SAML response contains incorrect XML", ex); } } throw new NoSamlResponseFoundException(); }
public void Saml2Response_Validate_FalseOnMissingReferenceInSignature() { var response = @"<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> </saml2p:Response>"; var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(response); var signedXml = new SignedXml(xmlDoc); signedXml.SigningKey = (RSACryptoServiceProvider)SignedXmlHelper.TestCert.PrivateKey; // The .NET implementation prevents creation of signatures without references (which is good) // but for this naughty test we want to create such a signature. Let's replace the real implementation // with a shim that bypasses the control. Code copied from reference source at // http://referencesource.microsoft.com/#System.Security/cryptography/xml/signedinfo.cs using (ShimsContext.Create()) { Func <SignedInfo, XmlDocument, XmlElement> signedInfoGetXml = (SignedInfo signedInfo, XmlDocument document) => { // Create the root element XmlElement signedInfoElement = document.CreateElement("SignedInfo", SignedXml.XmlDsigNamespaceUrl); if (!String.IsNullOrEmpty(signedInfo.Id)) { signedInfoElement.SetAttribute("Id", signedInfo.Id); } // Add the canonicalization method, defaults to SignedXml.XmlDsigNamespaceUrl // *** GetXml(XmlDocument, string) is internal, call it by reflection. *** // XmlElement canonicalizationMethodElement = signedInfo.CanonicalizationMethodObject.GetXml(document, "CanonicalizationMethod"); var transformGetXml = typeof(Transform).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) .Single(m => m.Name == "GetXml" && m.GetParameters().Length == 2); XmlElement canonicalizationMethodElement = (XmlElement)transformGetXml.Invoke(signedInfo.CanonicalizationMethodObject, new object[] { document, "CanonicalizationMethod" }); signedInfoElement.AppendChild(canonicalizationMethodElement); // Add the signature method if (String.IsNullOrEmpty(signedInfo.SignatureMethod)) { throw new CryptographicException("Cryptography_Xml_SignatureMethodRequired"); } XmlElement signatureMethodElement = document.CreateElement("SignatureMethod", SignedXml.XmlDsigNamespaceUrl); signatureMethodElement.SetAttribute("Algorithm", signedInfo.SignatureMethod); // Add HMACOutputLength tag if we have one if (signedInfo.SignatureLength != null) { XmlElement hmacLengthElement = document.CreateElement(null, "HMACOutputLength", SignedXml.XmlDsigNamespaceUrl); XmlText outputLength = document.CreateTextNode(signedInfo.SignatureLength); hmacLengthElement.AppendChild(outputLength); signatureMethodElement.AppendChild(hmacLengthElement); } signedInfoElement.AppendChild(signatureMethodElement); //*** This is the part of the original source that we want to bypass. *** //// Add the references //if (m_references.Count == 0) // throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_ReferenceElementRequired")); //for (int i = 0; i < m_references.Count; ++i) { // Reference reference = (Reference)m_references[i]; // signedInfoElement.AppendChild(reference.GetXml(document)); //} return(signedInfoElement); }; System.Security.Cryptography.Xml.Fakes.ShimSignedInfo.AllInstances.GetXml = (SignedInfo signedInfo) => { // Copy from SignedInfo.GetXml(XmlDocument) XmlDocument document = new XmlDocument(); document.PreserveWhitespace = true; return(signedInfoGetXml(signedInfo, document)); }; signedXml.ComputeSignature(); // Can't call SignedXml.GetXml(); as it calls original SignedInfo.GetXml(XmlDocument). This is // pasted / expanded code from SignedXml.GetXml that calls the above shim instead. // Create the Signature XmlElement signatureElement = (XmlElement)xmlDoc.CreateElement("Signature", SignedXml.XmlDsigNamespaceUrl); if (!String.IsNullOrEmpty(signedXml.Signature.Id)) { signatureElement.SetAttribute("Id", signedXml.Signature.Id); } // Add the SignedInfo if (signedXml.Signature.SignedInfo == null) { throw new CryptographicException("Cryptography_Xml_SignedInfoRequired"); } signatureElement.AppendChild(signedInfoGetXml(signedXml.Signature.SignedInfo, xmlDoc)); // Add the SignatureValue if (signedXml.Signature.SignatureValue == null) { throw new CryptographicException("Cryptography_Xml_SignatureValueRequired"); } XmlElement signatureValueElement = xmlDoc.CreateElement("SignatureValue", SignedXml.XmlDsigNamespaceUrl); signatureValueElement.AppendChild(xmlDoc.CreateTextNode(Convert.ToBase64String(signedXml.Signature.SignatureValue))); var m_signatureValueId = (string)typeof(Signature).GetField("m_signatureValueId", BindingFlags.Instance | BindingFlags.NonPublic) .GetValue(signedXml.Signature); if (!String.IsNullOrEmpty(m_signatureValueId)) { signatureValueElement.SetAttribute("Id", m_signatureValueId); } signatureElement.AppendChild(signatureValueElement); // Add the KeyInfo if (signedXml.Signature.KeyInfo.Count > 0) { signatureElement.AppendChild((XmlElement)typeof(KeyInfo).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) .Single(m => m.Name == "GetXml" && m.GetParameters().Length == 1) .Invoke(signedXml.Signature.KeyInfo, new object[] { xmlDoc })); } // Add the Objects foreach (Object obj in signedXml.Signature.ObjectList) { DataObject dataObj = obj as DataObject; if (dataObj != null) { signatureElement.AppendChild((XmlElement) typeof(DataObject).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic) .Single(m => m.Name == "GetXml" && m.GetParameters().Length == 1) .Invoke(dataObj, new object[] { xmlDoc })); } } xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(signatureElement, true)); var samlResponse = Saml2Response.Read(xmlDoc.OuterXml); samlResponse.Validate(SignedXmlHelper.TestKey).Should().BeFalse(); } }
public void Saml2Response_Read_ThrowsOnNonXml() { Action a = () => Saml2Response.Read("not xml"); a.ShouldThrow <XmlException>(); }