private static XmlDocument SignSamlRequest(AuthnRequestType request, string signatureNsPrefix, bool includePublicKey, string sslCertificateThumbprint) { X509Certificate2 cert = SignUtil.LoadCertificate(StoreName.My, StoreLocation.LocalMachine, sslCertificateThumbprint); XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("samlp", _samlProtocolNs); if (!string.IsNullOrEmpty(signatureNsPrefix)) { ns.Add(signatureNsPrefix, SignUtil.SignatureNamespace); } ns.Add("egovbga", _eauthExtNs); ns.Add("saml", _samlAssertionNs); XmlDocument doc = request.ToXmlDocument(ns); XmlElement signatureElement = SignUtil.Sign(doc, cert, signatureNsPrefix, includePublicKey); // Подписът вече е добавен в XML документа и по подразбиране е в края му. Според "saml-schema-protocol-2.0.xsd" обаче, // подписът трябва да бъде между елементите Issuer и Extensions. InsertAfter() го маха от старото място и го закача на новото. XmlNode issuerElement = doc.GetElementsByTagName(nameof(request.Issuer), _samlAssertionNs)[0]; doc.DocumentElement.InsertAfter(signatureElement, issuerElement); return(doc); }
public static AuthnRequestType GetSamlAuthnRequest(HttpContext context, ServiceProviderModel serviceProvider) { var authnRequest = new AuthnRequestType() { ID = AuthnRequestID = Guid.NewGuid().ToString(), Version = "2.0", IssueInstant = DateTime.UtcNow, ProtocolBinding = Saml2Constants.ProtocolBindings.HTTP_Post, AssertionConsumerServiceURL = serviceProvider.AssertionConsumerServiceUrl //$"{ context.Request.Scheme}://{context.Request.Host}{context.Request.PathBase}", }; authnRequest.Issuer = new NameIDType { Value = serviceProvider.Name }; authnRequest.NameIDPolicy = new NameIDPolicyType { Format = Saml2Constants.NameIdentifierFormats.Unspecified, AllowCreate = true }; authnRequest.RequestedAuthnContext = new RequestedAuthnContextType { Comparison = AuthnContextComparisonType.exact, Items = new string[] { "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" }, ItemsElementName = new[] { ItemsChoiceType7.AuthnContextClassRef } }; return(authnRequest); }
public Guid?GetSessionID(string userID, string vendor, string ipAddress) { AuthnRequestType auth = SSOTools.CreateAuthnRequest(userID, vendor, ipAddress); auth.Signature = SSOTools.Sign(auth, auth.ID, _clientCertificate); ChannelFactory <ISSOService> channelFactory = new ChannelFactory <ISSOService>("BasicHttpBinding_ISSOService"); ISSOService ssoClient = channelFactory.CreateChannel(); ResponseType r = ssoClient.GetSSO(new GetSSORequest { AuthnRequest = auth }).Response; channelFactory.Close(); if (SSOTools.VerifySignature(r, _serverCertificate)) { Dictionary <string, string> attributes = SSOTools.GetAttributes(r); Guid cleverDomeSessionGuid = new Guid(attributes["SessionID"]); //int timeOut = int.Parse(attributes["SessionTimeOut"]); // Time Out of the session. return(cleverDomeSessionGuid); } else { return(null); } }
/// <summary> /// Checks if the client request is valid and shows the login form /// </summary> /// <param name="samlRequest">SAML request by the client</param> //GET /SAML/AuthnRequest?SAMLRequest=base64encodedSAMLAuthnRequest public ActionResult AuthnRequest(string samlRequest) { if (samlRequest != null) { //Extract SAMLRequest XmlSerializer serializer = new XmlSerializer(typeof(AuthnRequestType)); string samlRequestXML = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(samlRequest)); AuthnRequestType authnRequest = (AuthnRequestType)serializer.Deserialize(new StringReader(samlRequestXML)); if (trusted.ContainsKey(authnRequest.Issuer.Value)) { ViewBag.requester = authnRequest.Issuer.Value; //user enters credentials return(View("Login")); } else { //Source of request not trusted return(View("NotTrusted")); } } return(View()); }
/// <summary> /// Creates a SAMLRequest object and serializes it. /// </summary> /// <returns>The serialized SAMLRequest object</returns> private string CreateSAMLRequest() { AuthnRequestType samlRequest = new AuthnRequestType() { ID = Guid.NewGuid().ToString(), Version = "2.0", IssueInstant = DateTime.UtcNow, Issuer = new NameIDType { Value = clientBaseAddress }, NameIDPolicy = new NameIDPolicyType { AllowCreate = true, Format = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" } }; XmlSerializer serializer = new XmlSerializer(typeof(AuthnRequestType)); StringWriter writer = new StringWriter(); serializer.Serialize(writer, samlRequest); string base64EncodedRequest = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(writer.ToString())); return(base64EncodedRequest); }
protected virtual async Task <ResponseType> BuildResponse(AuthnRequestType authnRequest, RelyingPartyAggregate relyingParty, NameIDPolicyValidationResult validationResult, CancellationToken cancellationToken) { var builder = SamlResponseBuilder.New() .AddAssertion(cb => { cb.SetIssuer(Constants.NameIdentifierFormats.PersistentIdentifier, _options.IDPId); cb.SetSubject(s => { s.SetNameId(validationResult.NameIdFormat, validationResult.NameIdValue); s.AddSubjectConfirmationBearer(DateTime.UtcNow, DateTime.UtcNow.AddSeconds(relyingParty.AssertionExpirationTimeInSeconds), inResponseTo: authnRequest.ID); }); cb.SetConditions(DateTime.UtcNow, DateTime.UtcNow.AddSeconds(relyingParty.AssertionExpirationTimeInSeconds), c => { c.AddAudienceRestriction(relyingParty.Id); }); foreach (var attr in validationResult.Attributes) { cb.AddAttributeStatementAttribute(attr.AttributeName, null, attr.Type, attr.Value); } }); if (await relyingParty.GetAssertionSigned(_entityDescriptorStore, cancellationToken)) { return(builder.SignAndBuild(_options.SigningCertificate, _options.SignatureAlg.Value, _options.CanonicalizationMethod)); } return(builder.Build()); }
public string RedirectableAuthRequest(SamlRequestOption requestOption, string xmlPrivateKey = "") { try { _logger.LogInformation("RedirectableAuthRequest -> initialize request for IDP {0}", requestOption.Destination); AuthnRequestType authnRequest = _authRequestTypeMapper.Map(requestOption); authnRequest.ProtocolBinding = SamlNamespaceHelper.SAML_PROTOCOL_BINDING_REDIRECT_NAMESPACE; _logger.LogInformation("RedirectableAuthRequest -> request created with id {0}", authnRequest.ID); string compressedRequest = WebUtility.UrlEncode(CompressRequest(authnRequest)); string sigAlg = WebUtility.UrlEncode(SignatureHelper.SIGNATURE_ALGORITHM_SHA256); _logger.LogInformation("RedirectableAuthRequest -> generating signature for id {0}...", authnRequest.ID); string tmpRequest = string.Concat("SAMLRequest=", compressedRequest, "&SigAlg=", sigAlg); string requestSignature = _signatureHelper.SignMessage(tmpRequest, requestOption.Certificate, xmlPrivateKey); _logger.LogInformation("RedirectableAuthRequest -> signature generated correctly"); string requestQueryString = string.Concat(tmpRequest, "&Signature=", requestSignature); _logger.LogDebug("RedirectableAuthRequest -> request id {0} - query string: {1}", authnRequest.ID, requestQueryString); _traceLogger.LogInformation("AuthnReq_ID: {0}|AuthnReq_IssueInstant: {1}|AuthnReq_QS: {2}", authnRequest.ID, authnRequest.IssueInstant, requestQueryString); return(requestQueryString); } catch (Exception ex) { _logger.LogError(ex, "RedirectableAuthRequest -> error on creating redirectable auth request"); throw; } }
protected virtual async Task <RelyingPartyAggregate> CheckRelyingParty(AuthnRequestType authnRequest, CancellationToken cancellationToken) { var relyingParty = await _relyingPartyRepository.Get(authnRequest.Issuer.Value, cancellationToken); if (relyingParty == null) { throw new SamlException(HttpStatusCode.NotFound, Saml.Constants.StatusCodes.Requester, string.Format(Global.UnknownIssuer, nameof(authnRequest.Issuer.Value))); } if (await relyingParty.GetAuthnRequestsSigned(_entityDescriptorStore, cancellationToken)) { var certificates = await relyingParty.GetSigningCertificates(_entityDescriptorStore, cancellationToken); foreach (var certificate in certificates) { if (SignatureHelper.CheckSignature(authnRequest.SerializeToXmlElement(), certificate)) { return(relyingParty); } } throw new SamlException(HttpStatusCode.BadRequest, Saml.Constants.StatusCodes.Requester, Global.BadAuthnRequestSignature); } if ((await relyingParty.GetAssertionLocation(_entityDescriptorStore, Saml.Constants.Bindings.HttpRedirect, cancellationToken)) == null) { throw new SamlException(HttpStatusCode.BadRequest, Saml.Constants.StatusCodes.UnsupportedBinding, Global.BadSPAssertionLocation); } return(relyingParty); }
protected virtual IAuthenticator CheckAuthnContextClassRef(AuthnRequestType authnRequest) { if (authnRequest.RequestedAuthnContext != null) { AuthnContextComparisonType comparison = AuthnContextComparisonType.exact; // TODO : Support other comparison. if (authnRequest.RequestedAuthnContext.ComparisonSpecified) { comparison = authnRequest.RequestedAuthnContext.Comparison; } if (authnRequest.RequestedAuthnContext.Items == null || !authnRequest.RequestedAuthnContext.Items.Any()) { throw new SamlException(HttpStatusCode.BadRequest, Saml.Constants.StatusCodes.NoAuthnContext, Global.MissingAuthnContextClassRef); } var items = authnRequest.RequestedAuthnContext.Items; switch (comparison) { case AuthnContextComparisonType.exact: var auth = _authenticators.FirstOrDefault(a => items.Contains(a.AuthnContextClassRef)); if (auth == null) { throw new SamlException(HttpStatusCode.BadRequest, Saml.Constants.StatusCodes.NoAuthnContext, string.Format(Global.UnknownAuthContextClassRef, string.Join(",", items))); } return(auth); } } return(_authenticators.First(a => a.AuthnContextClassRef == _options.DefaultAuthnContextClassRef)); }
public static ResponseType CreateErrorSamlResponse(AuthnRequestType samlAuthRequest) { var samlResponse = new ResponseType(true) { ID = "_" + Guid.NewGuid(), Version = "2.0", IssueInstant = System.DateTime.UtcNow, Destination = samlAuthRequest.AssertionConsumerServiceURL, InResponseTo = samlAuthRequest.ID, Issuer = new NameIDType() { Value = ISSUER }, Status = new StatusType() { StatusCode = new StatusCodeType() { Value = Saml2Constants.StatusCodes.AuthnFailed } } }; samlResponse.Items = new AssertionType[] { }; return(samlResponse); }
public static ResponseType CreateSuccessSamlResponse(AuthnRequestType samlAuthRequest) { var samlResponse = new ResponseType(true) { ID = "_" + Guid.NewGuid(), Version = "2.0", IssueInstant = System.DateTime.UtcNow, Destination = samlAuthRequest.AssertionConsumerServiceURL, InResponseTo = samlAuthRequest.ID, Issuer = new NameIDType() { Value = ISSUER }, Status = new StatusType() { StatusCode = new StatusCodeType() { Value = Saml2Constants.StatusCodes.Success } } }; samlResponse.Items = new AssertionType[] { CreateSamlAssertion(samlAuthRequest, _context.User.FindFirst("Username").Value) }; return(samlResponse); }
public static XmlDocument toXmlDocument(AuthnRequestType authnRequest) { XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("samlp", Saml2Constants.SAML2_PROTOCOL_NAMESPACE); ns.Add("saml", Saml2Constants.SAML2_ASSERTION_NAMESPACE); XmlRootAttribute xRoot = new XmlRootAttribute(); xRoot.ElementName = "AuthnRequest"; xRoot.Namespace = Saml2Constants.SAML2_PROTOCOL_NAMESPACE; XmlSerializer serializer = new XmlSerializer(typeof(AuthnRequestType), xRoot); MemoryStream memoryStream = new MemoryStream(); XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); serializer.Serialize(xmlTextWriter, authnRequest, ns); XmlDocument document = new XmlDocument(); memoryStream.Seek(0, SeekOrigin.Begin); document.Load(memoryStream); xmlTextWriter.Close(); return(document); }
public string PostableAuthRequest(SamlRequestOption requestOption, string xmlPrivateKey = "") { try { _logger.LogInformation("PostableAuthRequest -> initialize request for IDP {0}", requestOption.Destination); _logger.LogInformation("PostableAuthRequest -> creating request for authentication..."); AuthnRequestType authnRequest = _authRequestTypeMapper.Map(requestOption); _logger.LogInformation("PostableAuthRequest -> request created with id {0}", authnRequest.ID); XmlDocument xmlRequest = new XmlDocument(); xmlRequest.LoadXml(authnRequest.ToXmlString()); _logger.LogInformation("PostableAuthRequest -> generating signature for id {0}...", authnRequest.ID); XmlElement signatureElement = _signatureHelper.GetXmlAuthRequestSignature(xmlRequest, requestOption.Certificate, xmlPrivateKey); xmlRequest.DocumentElement.InsertAfter(signatureElement, xmlRequest.DocumentElement.ChildNodes[0]); _logger.LogInformation("PostableAuthRequest -> signature generated correctly"); _logger.LogDebug("PostableAuthRequest -> request id: {0} xml: {1}", authnRequest.ID, xmlRequest.OuterXml); _traceLogger.LogInformation("AuthnReq_ID: {0}|AuthnReq_IssueInstant: {1}|AuthnReq_SAML: {2}", authnRequest.ID, authnRequest.IssueInstant, xmlRequest.OuterXml); return(Convert.ToBase64String(Encoding.UTF8.GetBytes(xmlRequest.OuterXml))); } catch (Exception ex) { _logger.LogError(ex, "PostableAuthRequest -> error on creating postable auth request"); throw; } }
private AuthnRequestBuilder(string providerName) { _authRequest = new AuthnRequestType { ID = $"pfx{Guid.NewGuid().ToString()}", Version = Constants.SamlVersion, IssueInstant = DateTime.UtcNow, ProviderName = providerName }; }
public static XmlDocument CreateSamlRequest( string eAuthUrl, string callerOid, string callerName, string idPrefix, // Не трябва да започва с цифра. string requestUrl, string callbackUrl, string requestedServiceOid, string requestedProviderOid, // Параметри за подписването. string signatureNsPrefix, bool includePublicKey, string sslCertificateThumbprint, out string id) { DateTime now = DateTime.Now; // Закръгление до милисекунди. now = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond); id = $"{idPrefix ?? "ID"}_{now.ToString(_requestIdDateTimeFormat, System.Globalization.CultureInfo.InvariantCulture)}"; AuthnRequestType authnRequest = new AuthnRequestType { ID = id, Version = "2.0", // Според дадения пример, IssueInstant="2015-06-29T03:00:09", т.е. стойността е закръглена до секунда и няма time zone. IssueInstant = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, DateTimeKind.Unspecified), ProtocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", Destination = eAuthUrl, ForceAuthn = true, ForceAuthnSpecified = true, IsPassive = false, IsPassiveSpecified = true, ProviderName = callerName, AssertionConsumerServiceURL = callbackUrl, // Информационна система, която инициира автентификация. Issuer = new NameIDType { SPProvidedID = callerOid, Value = requestUrl }, // Според документацията: версия на услугата + информационна система, която я предоставя. // Реално работи също и с информационна система + администрация-собственик. Extensions = new ExtensionsType { RequestedService = new RequestedServiceType { Service = requestedServiceOid, Provider = requestedProviderOid } } }; return(SignSamlRequest(authnRequest, signatureNsPrefix, includePublicKey, sslCertificateThumbprint)); }
public XmlDocument EndpointMapSamlRequest(Endpoint endpoint) { AuthnRequestType request = new AuthnRequestType { ID = Helper.GuidAsIdString(endpoint.Id), Version = Saml.Names.SAMLVersion, ProviderName = endpoint.Description, Destination = endpoint.Login, IssueInstant = DateTime.UtcNow, Issuer = new NameIDType { Value = endpoint.Requestor }, AssertionConsumerServiceURL = endpoint.Requestor, ProtocolBinding = Saml.Names.SAMLNamesProtocolBindingPOST, ForceAuthn = false, ForceAuthnSpecified = true, //When ForceAuthn true, user will be forced to re-authenticate, even if valid session IsPassive = false, IsPassiveSpecified = true, //When IsPassive true, authenticate user silently, without user interaction, using the session cookie if one exists Subject = new SubjectType { Items = new object[] { new NameIDType { Value = endpoint.Id, Format = Saml.Names.SAMLNamesFormatIssuerEntity }, new SubjectConfirmationType { Method = Saml.Names.SAMLNamesSubjectConfirmationBaerer, SubjectConfirmationData = new SubjectConfirmationDataType { NotOnOrAfter = DateTime.UtcNow.AddMinutes(Saml.Names.SAMLAssertionExpirationMinutes), Recipient = endpoint.Requestor } } } }, Conditions = new ConditionsType { NotBefore = DateTime.UtcNow, NotBeforeSpecified = true, NotOnOrAfter = DateTime.UtcNow.AddMinutes(Saml.Names.SAMLAssertionExpirationMinutes), NotOnOrAfterSpecified = true, Items = new ConditionAbstractType[] { new AudienceRestrictionType { Audience = new string[] { endpoint.Referrer } } } } }; XmlDocument xmlRequest = Saml.Helper.SerializeAndSignSAMLType <AuthnRequestType>(request, request.ID); return(xmlRequest); }
private AuthnRequestType AuthnRequestTypeCreate() { AuthnRequestType _request = new AuthnRequestType(); _request.ID = "_" + Guid.NewGuid().ToString(); _request.Version = "2.0"; _request.IssueInstant = DateTime.UtcNow; _request.Destination = ama_Destination; _request.Consent = "urn:oasis:names:tc:SAML:2.0:consent:unspecified"; _request.ProtocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; _request.AssertionConsumerServiceURL = ama_AssertionConsumerServiceURL; _request.ProviderName = ama_ProviderName; _request.Issuer = new NameIDType(); _request.Issuer.Value = "http://localhost:64181"; _request.Extensions = new ExtensionsType(); _request.Extensions.Any = new XmlElement[] { RequestAttributeCreate() }; return(_request); }
public string GetNewTransaction(AuthenticationRequest authenticationRequest) { if (authenticationRequest == null) { throw new ArgumentNullException("authenticationRequest"); } var authnRequest = new AuthnRequestType { ID = authenticationRequest.MerchantReference, Version = Constants.BankIdAuthnRequestTypeVersion, IssueInstant = authenticationRequest.CreateDateTimestamp, ForceAuthn = true, IsPassive = false, ProtocolBinding = Constants.BankIdProtocolBinding, AssertionConsumerServiceURL = _configuration.MerchantReturnUrl.AbsoluteUri, AttributeConsumingServiceIndex = (ushort)authenticationRequest.RequestedServiceId, AttributeConsumingServiceIndexSpecified = true, Issuer = new NameIDType { Value = _configuration.MerchantId }, Conditions = new ConditionsType(), RequestedAuthnContext = new RequestedAuthnContextType { Comparison = AuthnContextComparisonType.minimum, ComparisonSpecified = true, ItemsElementName = new[] { ItemsChoiceType7.AuthnContextClassRef }, Items = new[] { GetLevelOfAssuranceString(authenticationRequest.AssuranceLevel) } }, Extensions = GetAuthnRequestExtensions(authenticationRequest), Scoping = new ScopingType() }; return(DateTimeHelper.ProcessDateTimes(authnRequest.Serialize(), _dateTimeElementNames)); }
private void ValidateSamlRequest(AuthnRequestType samlAuthRequest) { throw new NotImplementedException(); }
/// <summary> /// Generates a SAML v2.0 Authentication Request with HTTP Browser Post Binding. /// The return string containing the request is NOT Base64 encoded. /// </summary> /// <param name="linkIDContext">the linkID authentication/payment configuration</param> /// <returns>SAML request</returns> public static AuthnRequestType generateAuthnRequest(LinkIDAuthenticationContext linkIDContext) { AuthnRequestType authnRequest = new AuthnRequestType(); authnRequest.ForceAuthn = true; authnRequest.ID = Guid.NewGuid().ToString(); authnRequest.Version = "2.0"; authnRequest.IssueInstant = DateTime.UtcNow; NameIDType issuer = new NameIDType(); issuer.Value = linkIDContext.applicationName; authnRequest.Issuer = issuer; NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); nameIdPolicy.AllowCreate = true; nameIdPolicy.AllowCreateSpecified = true; authnRequest.NameIDPolicy = nameIdPolicy; Dictionary <string, string> deviceContextMap = linkIDContext.getDeviceContextMap(); DeviceContextType deviceContext = null; if (null != deviceContextMap && deviceContextMap.Count > 0) { deviceContext = new DeviceContextType(); List <AttributeType> attributes = new List <AttributeType>(); foreach (string deviceContextKey in deviceContextMap.Keys) { string deviceContextValue = deviceContextMap[deviceContextKey]; AttributeType attribute = new AttributeType(); attribute.Name = deviceContextKey; attribute.AttributeValue = new object[] { deviceContextValue }; attributes.Add(attribute); deviceContext.Items = attributes.ToArray(); } } SubjectAttributesType subjectAttributes = null; if (null != linkIDContext.attributeSuggestions && linkIDContext.attributeSuggestions.Count > 0) { subjectAttributes = new SubjectAttributesType(); List <AttributeType> attributes = new List <AttributeType>(); foreach (string attributeName in linkIDContext.attributeSuggestions.Keys) { List <object> values = linkIDContext.attributeSuggestions[attributeName]; AttributeType attribute = new AttributeType(); attribute.Name = attributeName; attribute.AttributeValue = values.ToArray(); attributes.Add(attribute); subjectAttributes.Items = attributes.ToArray(); } } PaymentContextType paymentContextType = null; if (null != linkIDContext.paymentContext) { Dictionary <String, String> paymentContextDict = linkIDContext.paymentContext.toDictionary(); paymentContextType = new PaymentContextType(); List <AttributeType> attributes = new List <AttributeType>(); foreach (string paymentContextKey in paymentContextDict.Keys) { string value = paymentContextDict[paymentContextKey]; AttributeType attribute = new AttributeType(); attribute.Name = paymentContextKey; attribute.AttributeValue = new object[] { value }; attributes.Add(attribute); paymentContextType.Items = attributes.ToArray(); } } CallbackType callbackType = null; if (null != linkIDContext.callback) { Dictionary <String, String> callbackDict = linkIDContext.callback.toDictionary(); callbackType = new CallbackType(); List <AttributeType> attributes = new List <AttributeType>(); foreach (string callbackKey in callbackDict.Keys) { string value = callbackDict[callbackKey]; AttributeType attribute = new AttributeType(); attribute.Name = callbackKey; attribute.AttributeValue = new object[] { value }; attributes.Add(attribute); callbackType.Items = attributes.ToArray(); } } if (null != deviceContext || null != subjectAttributes || null != paymentContextType || null != callbackType) { ExtensionsType extensions = new ExtensionsType(); List <XmlElement> extensionsList = new List <XmlElement>(); if (null != subjectAttributes) { extensionsList.Add(toXmlElement(subjectAttributes)); } if (null != deviceContext) { extensionsList.Add(toXmlElement(deviceContext)); } if (null != paymentContextType) { extensionsList.Add(toXmlElement(paymentContextType)); } if (null != callbackType) { extensionsList.Add(toXmlElement(callbackType)); } extensions.Any = extensionsList.ToArray(); authnRequest.Extensions = extensions; } return(authnRequest); }
/// <summary> /// Validates the authn response. /// </summary> /// <param name="response">The response.</param> /// <param name="request">The request.</param> /// <param name="metadataIdp">The metadata idp.</param> /// <exception cref="Exception"> /// </exception> public static void ValidateAuthnResponse(this ResponseType response, AuthnRequestType request, EntityDescriptor metadataIdp) { // Verify signature var xmlDoc = response.SerializeToXmlDoc(); BusinessValidation.ValidationCondition(() => response.Status == null, ErrorLocalization.StatusNotValid); BusinessValidation.ValidationCondition(() => response.Status.StatusCode == null, ErrorLocalization.StatusCodeNotValid); if (!response.Status.StatusCode.Value.Equals(SamlConst.Success, StringComparison.InvariantCultureIgnoreCase)) { if (int.TryParse(response.Status.StatusMessage?.Replace("ErrorCode nr", ""), out var errorCode)) { switch (errorCode) { case 19: throw new Exception(ErrorLocalization._19); case 20: throw new Exception(ErrorLocalization._20); case 21: throw new Exception(ErrorLocalization._21); case 22: throw new Exception(ErrorLocalization._22); case 23: throw new Exception(ErrorLocalization._23); case 25: throw new Exception(ErrorLocalization._25); default: break; } } throw new Exception(ErrorLocalization.StatusCodeNotValid); } BusinessValidation.ValidationCondition(() => response.Signature == null, ErrorLocalization.ResponseSignatureNotFound); BusinessValidation.ValidationCondition(() => response?.GetAssertion() == null, ErrorLocalization.ResponseAssertionNotFound); BusinessValidation.ValidationCondition(() => response.GetAssertion()?.Signature == null, ErrorLocalization.AssertionSignatureNotFound); BusinessValidation.ValidationCondition(() => response.GetAssertion().Signature.KeyInfo.GetX509Data().GetX509Certificate() != response.Signature.KeyInfo.GetX509Data().GetX509Certificate(), ErrorLocalization.AssertionSignatureDifferent); var metadataXmlDoc = metadataIdp.SerializeToXmlDoc(); BusinessValidation.ValidationCondition(() => XmlHelpers.VerifySignature(xmlDoc, metadataXmlDoc), ErrorLocalization.InvalidSignature); var respSigningCert = X509Helpers.AddCertificateHeaders(response.Signature.KeyInfo.GetX509Data().GetX509Certificate()); using var responseCertificate = new X509Certificate2(Encoding.UTF8.GetBytes(respSigningCert)); var assertSigningCert = X509Helpers.AddCertificateHeaders(response.GetAssertion()?.Signature.KeyInfo.GetX509Data().GetX509Certificate()); using var assertionCertificate = new X509Certificate2(Encoding.UTF8.GetBytes(assertSigningCert)); var idpSigningCert = X509Helpers.AddCertificateHeaders(metadataIdp.IDPSSODescriptor.KeyDescriptor.KeyInfo.X509Data.X509Certificate); using var idpCertificate = new X509Certificate2(Encoding.UTF8.GetBytes(idpSigningCert)); BusinessValidation.ValidationCondition(() => responseCertificate.Thumbprint != idpCertificate.Thumbprint, ErrorLocalization.ResponseSignatureNotValid); BusinessValidation.ValidationCondition(() => assertionCertificate.Thumbprint != idpCertificate.Thumbprint, ErrorLocalization.AssertionSignatureNotValid); BusinessValidation.ValidationCondition(() => response.Version != SamlConst.Version, ErrorLocalization.VersionNotValid); BusinessValidation.ValidationNotNullNotWhitespace(response.ID, nameof(response.ID)); BusinessValidation.ValidationNotNull(response.GetAssertion()?.GetAttributeStatement(), ErrorFields.Assertion); BusinessValidation.ValidationCondition(() => response.GetAssertion().GetAttributeStatement()?.GetAttributes()?.Count() == 0, ErrorLocalization.AttributeNotFound); BusinessValidation.ValidationCondition(() => response.GetAssertion().GetAttributeStatement()?.GetAttributes()?.Any(a => a.AttributeValue == null) ?? false, ErrorLocalization.AttributeNotFound); var listAttribute = new List <string> { SamlConst.fiscalNumber, SamlConst.digitalAddress, SamlConst.name, SamlConst.familyName, SamlConst.email, SamlConst.address, SamlConst.companyName, SamlConst.countyOfBirth, SamlConst.dateOfBirth, SamlConst.expirationDate, SamlConst.fiscalNumber, SamlConst.gender, SamlConst.idCard, SamlConst.ivaCode, SamlConst.mobilePhone, SamlConst.placeOfBirth, SamlConst.registeredOffice, SamlConst.spidCode, }; var attribute = response.GetAssertion().GetAttributeStatement().GetAttributes(); List <string> attributeNames = new List <string>(); attributeNames.AddRange(attribute.Where(x => !string.IsNullOrWhiteSpace(x.Name) && !x.Name.StartsWith("urn")).Select(x => x.Name).ToList()); BusinessValidation.ValidationCondition(() => attributeNames.Count() == 0, ErrorLocalization.AttributeRequiredNotFound); if (attributeNames.Count() > 0) { BusinessValidation.ValidationCondition(() => attributeNames.Any(x => !listAttribute.Contains(x)), ErrorLocalization.AttributeRequiredNotFound); } else { listAttribute.Add(SamlConst.firstname); listAttribute.Add(SamlConst.surname); listAttribute.Add(SamlConst.mail); attributeNames.AddRange(attribute.Where(x => !string.IsNullOrWhiteSpace(x.FriendlyName)).Select(x => x.FriendlyName).ToList()); BusinessValidation.ValidationCondition(() => attributeNames.Count() == 0, ErrorLocalization.AttributeRequiredNotFound); if (attributeNames.Count() > 0) { BusinessValidation.ValidationCondition(() => listAttribute.All(x => !attributeNames.Contains(x)), ErrorLocalization.AttributeRequiredNotFound); } } BusinessValidation.ValidationCondition(() => response.IssueInstant == default, ErrorLocalization.IssueInstantMissing); DateTimeOffset issueIstant = new DateTimeOffset(response.IssueInstant); var issueIstantRequest = DateTimeOffset.Parse(request.IssueInstant); BusinessValidation.ValidationCondition(() => (issueIstant - issueIstantRequest).Duration() > TimeSpan.FromMinutes(10), ErrorLocalization.IssueIstantDifferentFromRequest); BusinessValidation.ValidationNotNullNotWhitespace(response.InResponseTo, nameof(response.InResponseTo)); BusinessValidation.ValidationNotNullNotWhitespace(response.Destination, nameof(response.Destination)); if (!string.IsNullOrWhiteSpace(request.AssertionConsumerServiceURL)) { BusinessValidation.ValidationCondition(() => !response.Destination.Equals(request.AssertionConsumerServiceURL), string.Format(ErrorLocalization.DifferentFrom, nameof(response.Destination), nameof(request.AssertionConsumerServiceURL))); } BusinessValidation.ValidationNotNullNotEmpty(response.Status, nameof(response.Status)); BusinessValidation.ValidationCondition(() => response.Issuer == null, ErrorLocalization.IssuerNotSpecified); BusinessValidation.ValidationCondition(() => string.IsNullOrWhiteSpace(response.Issuer?.Value), ErrorLocalization.IssuerMissing); BusinessValidation.ValidationCondition(() => !response.Issuer.Value.Equals(metadataIdp.EntityID, StringComparison.InvariantCultureIgnoreCase), ErrorLocalization.IssuerDifferentFromEntityId); BusinessValidation.ValidationCondition(() => !string.IsNullOrWhiteSpace(response.Issuer.Format) && !response.Issuer.Format.Equals(SamlConst.IssuerFormat), ErrorLocalization.IssuerFormatDifferent); BusinessValidation.ValidationNotNullNotEmpty(response?.GetAssertion(), ErrorFields.Assertion); BusinessValidation.ValidationCondition(() => response.GetAssertion().ID == null, string.Format(ErrorLocalization.Missing, ErrorFields.ID)); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().ID, ErrorFields.ID); BusinessValidation.ValidationCondition(() => response.GetAssertion().Version != SamlConst.Version, string.Format(ErrorLocalization.DifferentFrom, ErrorFields.Version, SamlConst.Version)); BusinessValidation.ValidationCondition(() => response.GetAssertion().IssueInstant == null, string.Format(ErrorLocalization.NotSpecified, ErrorFields.IssueInstant)); DateTimeOffset assertionIssueIstant = response.GetAssertion().IssueInstant; BusinessValidation.ValidationCondition(() => assertionIssueIstant > issueIstantRequest.AddMinutes(10), ErrorLocalization.IssueIstantAssertionGreaterThanRequest); BusinessValidation.ValidationCondition(() => assertionIssueIstant < issueIstantRequest, ErrorLocalization.IssueIstantAssertionLessThanRequest); BusinessValidation.ValidationCondition(() => (assertionIssueIstant - issueIstantRequest).Duration() > TimeSpan.FromMinutes(10), assertionIssueIstant > issueIstantRequest ? ErrorLocalization.IssueIstantAssertionGreaterThanRequest : ErrorLocalization.IssueIstantAssertionLessThanRequest); BusinessValidation.ValidationNotNull(response.GetAssertion().Subject, ErrorFields.Subject); BusinessValidation.ValidationNotNull(response.GetAssertion().Subject?.Items, ErrorFields.Subject); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Subject?.GetNameID()?.Value, ErrorFields.NameID); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Subject?.GetNameID()?.Format, ErrorFields.Format); BusinessValidation.ValidationCondition(() => !response.GetAssertion().Subject.GetNameID().Format.Equals(request.NameIDPolicy.Format), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.Format)); BusinessValidation.ValidationCondition(() => response.GetAssertion().Subject.GetNameID().NameQualifier == null, string.Format(ErrorLocalization.NotSpecified, "Assertion.NameID.NameQualifier")); BusinessValidation.ValidationCondition(() => String.IsNullOrWhiteSpace(response.GetAssertion().Subject.GetNameID().NameQualifier), string.Format(ErrorLocalization.Missing, "Assertion.NameID.NameQualifier")); BusinessValidation.ValidationNotNullNotEmpty(response.GetAssertion().Subject.GetSubjectConfirmation(), ErrorFields.SubjectConfirmation); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Subject.GetSubjectConfirmation().Method, ErrorFields.Method); BusinessValidation.ValidationCondition(() => !response.GetAssertion().Subject.GetSubjectConfirmation().Method.Equals(SamlConst.Method), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.Method)); BusinessValidation.ValidationNotNullNotEmpty(response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData, ErrorFields.SubjectConfirmationData); BusinessValidation.ValidationCondition(() => response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.Recipient == null, string.Format(ErrorLocalization.NotSpecified, "Assertion.SubjectConfirmationData.Recipient")); BusinessValidation.ValidationCondition(() => string.IsNullOrWhiteSpace(response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.Recipient), string.Format(ErrorLocalization.Missing, "Assertion.SubjectConfirmationData.Recipient")); BusinessValidation.ValidationCondition(() => !response.Destination.Equals(response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.Recipient, StringComparison.OrdinalIgnoreCase), ErrorLocalization.InvalidDestination); if (!string.IsNullOrWhiteSpace(request.AssertionConsumerServiceURL)) { BusinessValidation.ValidationCondition(() => !response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.Recipient.Equals(request.AssertionConsumerServiceURL), string.Format(ErrorLocalization.DifferentFrom, "Assertion.SubjectConfirmationData.Recipient", "Request")); } BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.InResponseTo, ErrorFields.InResponseTo); BusinessValidation.ValidationCondition(() => !response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.InResponseTo.Equals(request.ID), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.InResponseTo)); BusinessValidation.ValidationCondition(() => response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.NotOnOrAfter == null, string.Format(ErrorLocalization.NotSpecified, "Assertion.SubjectConfirmationData.NotOnOrAfter")); BusinessValidation.ValidationCondition(() => response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.NotOnOrAfter == DateTime.MinValue, string.Format(ErrorLocalization.Missing, "Assertion.SubjectConfirmationData.NotOnOrAfter")); DateTimeOffset notOnOrAfter = new DateTimeOffset(response.GetAssertion().Subject.GetSubjectConfirmation().SubjectConfirmationData.NotOnOrAfter); BusinessValidation.ValidationCondition(() => notOnOrAfter < DateTimeOffset.UtcNow, ErrorLocalization.NotOnOrAfterLessThenRequest); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Issuer?.Value, ErrorFields.Issuer); BusinessValidation.ValidationCondition(() => !response.GetAssertion().Issuer.Value.Equals(metadataIdp.EntityID), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.Issuer)); BusinessValidation.ValidationCondition(() => response.GetAssertion().Issuer.Format == null, string.Format(ErrorLocalization.NotSpecified, "Assertion.Issuer.Format")); BusinessValidation.ValidationCondition(() => string.IsNullOrWhiteSpace(response.GetAssertion().Issuer.Format), string.Format(ErrorLocalization.Missing, "Assertion.Issuer.Format")); BusinessValidation.ValidationCondition(() => !response.GetAssertion().Issuer.Format.Equals(request.Issuer.Format), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.Format)); BusinessValidation.ValidationCondition(() => response.GetAssertion().Conditions == null, string.Format(ErrorLocalization.NotSpecified, "Assertion.Conditions")); BusinessValidation.ValidationCondition(() => response.GetAssertion().Conditions.GetAudienceRestriction() == null && string.IsNullOrWhiteSpace(response.GetAssertion().Conditions.NotBefore) && string.IsNullOrWhiteSpace(response.GetAssertion().Conditions.NotOnOrAfter), string.Format(ErrorLocalization.Missing, "Assertion.Conditions")); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Conditions.NotOnOrAfter, ErrorFields.NotOnOrAfter); DateTimeOffset notOnOrAfterCondition = SamlDefaultSettings.ParseExact(response.GetAssertion().Conditions.NotOnOrAfter, "Assertion.Conditions.NotOnOrAfter"); BusinessValidation.ValidationCondition(() => notOnOrAfterCondition < DateTimeOffset.UtcNow, ErrorLocalization.NotOnOrAfterLessThenRequest); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Conditions.NotBefore, ErrorFields.NotBefore); DateTimeOffset notBefore = SamlDefaultSettings.ParseExact(response.GetAssertion().Conditions.NotBefore, "Assertion.Conditions.NotBefore"); BusinessValidation.ValidationCondition(() => notBefore > DateTimeOffset.UtcNow, ErrorLocalization.NotBeforeGreaterThenRequest); BusinessValidation.ValidationCondition(() => response.GetAssertion().Conditions.GetAudienceRestriction() == null, string.Format(ErrorLocalization.Missing, "Assertion.Conditions.AudienceRestriction")); BusinessValidation.ValidationNotNullNotWhitespace(response.GetAssertion().Conditions.GetAudienceRestriction().Audience?.First(), ErrorFields.Audience); BusinessValidation.ValidationCondition(() => !(response.GetAssertion().Conditions.GetAudienceRestriction().Audience.First()?.Equals(request.Issuer.Value) ?? false), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.Audience)); BusinessValidation.ValidationCondition(() => response.GetAssertion().GetAuthnStatement() == null, string.Format(ErrorLocalization.NotSpecified, ErrorFields.AuthnStatement)); BusinessValidation.ValidationCondition(() => response.GetAssertion().GetAuthnStatement().AuthnInstant == DateTime.MinValue && string.IsNullOrWhiteSpace(response.GetAssertion().GetAuthnStatement().SessionIndex) && response.GetAssertion().GetAuthnStatement().AuthnContext == null, string.Format(ErrorLocalization.Missing, ErrorFields.AuthnStatement)); BusinessValidation.ValidationNotNull(response.GetAssertion().GetAuthnStatement().AuthnContext, ErrorFields.AuthnContext); BusinessValidation.ValidationNotNull(response.GetAssertion().GetAuthnStatement().AuthnContext.Items, ErrorFields.AuthnContext); BusinessValidation.ValidationNotNull(response.GetAssertion().GetAuthnStatement().AuthnContext.ItemsElementName, ErrorFields.AuthnContext); BusinessValidation.ValidationCondition(() => response.GetAssertion().GetAuthnStatement().AuthnContext.GetAuthnContextClassRef() == null, string.Format(ErrorLocalization.NotSpecified, "AuthnStatement.AuthnContext.AuthnContextClassRef")); BusinessValidation.ValidationCondition(() => string.IsNullOrWhiteSpace(response.GetAssertion().GetAuthnStatement().AuthnContext.GetAuthnContextClassRef()), string.Format(ErrorLocalization.Missing, "AuthnStatement.AuthnContext.AuthnContextClassRef")); BusinessValidation.ValidationCondition(() => !response.GetAssertion().GetAuthnStatement().AuthnContext.GetAuthnContextClassRef().Equals(request.RequestedAuthnContext.Items[0]), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.AuthnContextClassRef)); BusinessValidation.ValidationCondition(() => !listAuthRefValid.Contains(response.GetAssertion().GetAuthnStatement().AuthnContext.GetAuthnContextClassRef()), string.Format(ErrorLocalization.ParameterNotValid, ErrorFields.AuthnContextClassRef)); }
private static AssertionType CreateSamlAssertion(AuthnRequestType samlAuthRequest, string username) { var assertion = new AssertionType { Version = "2.0", IssueInstant = DateTime.UtcNow, ID = "_" + Guid.NewGuid(), Issuer = new NameIDType() { Value = $"{_context.Request.Scheme}://{_context.Request.Host}{_context.Request.PathBase}" } }; //Assertion Subject var subject = new SubjectType(); var subjectNameIdentifier = new NameIDType() { Value = username, Format = Saml2Constants.NameIdentifierFormats.Unspecified }; var subjectConfirmation = new SubjectConfirmationType() { Method = Saml2Constants.SubjectConfirmationMethods.HolderOfKey, SubjectConfirmationData = new SubjectConfirmationDataType() { NotOnOrAfter = DateTime.UtcNow.AddMinutes(ASSERTION_TIMEOUT_IN_MINUTES), Recipient = samlAuthRequest.AssertionConsumerServiceURL, InResponseTo = samlAuthRequest.ID } }; subject.Items = new object[] { subjectNameIdentifier, subjectConfirmation }; assertion.Subject = subject; //Assertion Conditions var conditions = new ConditionsType { NotBefore = DateTime.UtcNow, NotBeforeSpecified = true, NotOnOrAfter = DateTime.UtcNow.AddMinutes(ASSERTION_TIMEOUT_IN_MINUTES), NotOnOrAfterSpecified = true, //TODO: samlAuthRequest.Issuer.Value should be replaced with Items = new ConditionAbstractType[] { new AudienceRestrictionType() { Audience = new string[] { samlAuthRequest.Issuer.Value } } } }; assertion.Conditions = conditions; //Assertion AuthnStatement var authStatement = new AuthnStatementType() { AuthnInstant = DateTime.UtcNow, SessionIndex = assertion.ID }; var context = new AuthnContextType(); context.ItemsElementName = new[] { ItemsChoiceType5.AuthnContextClassRef }; context.Items = new object[] { "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified" }; authStatement.AuthnContext = context; //Assertion AttributeStatement var attributeStatement = new AttributeStatementType(); attributeStatement.Items = new AttributeType[] { //Add as many attributes as you want here, these are the user details that service provider wants, we can customise the attributes required // on the basis of service provider that requires this assertion new AttributeType { Name = "username", AttributeValue = username, NameFormat = "urn:oasis:names:tc:SAML:2.0:attrname-format:basic" } }; assertion.Items = new StatementAbstractType[] { authStatement, attributeStatement }; return(assertion); }
public void TestAuthnRequest() { AuthnRequestType authnRequest = new AuthnRequestType(); authnRequest.ID = "test-id"; authnRequest.AssertionConsumerServiceURL = "http://test.assertion.consumer"; authnRequest.Destination = "http://destination"; authnRequest.ForceAuthn = true; authnRequest.ProtocolBinding = "urn:test:protocol:binding"; authnRequest.Version = "2.0"; authnRequest.IssueInstant = DateTime.Now.ToUniversalTime(); NameIDType issuer = new NameIDType(); issuer.Value = "test-issuer"; authnRequest.Issuer = issuer; NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); nameIdPolicy.AllowCreate = true; nameIdPolicy.AllowCreateSpecified = true; authnRequest.NameIDPolicy = nameIdPolicy; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("samlp", "urn:oasis:names:tc:SAML:2.0:protocol"); ns.Add("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); //ns.Add("ds", "http://www.w3.org/2000/09/xmldsig#"); XmlRootAttribute xRoot = new XmlRootAttribute(); xRoot.ElementName = "AuthnRequest"; xRoot.Namespace = "urn:oasis:names:tc:SAML:2.0:protocol"; XmlSerializer serializer = new XmlSerializer(typeof(AuthnRequestType), xRoot); MemoryStream memoryStream = new MemoryStream(); XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); serializer.Serialize(xmlTextWriter, authnRequest, ns); memoryStream = (MemoryStream)xmlTextWriter.BaseStream; string result = new UTF8Encoding().GetString(memoryStream.ToArray()); Console.WriteLine("result: " + result); XmlDocument document = new XmlDocument(); memoryStream.Seek(0, SeekOrigin.Begin); document.Load(memoryStream); String xmlString = document.OuterXml; Console.WriteLine("DOM result: " + xmlString); RSACryptoServiceProvider Key = new RSACryptoServiceProvider(); SignedXml signedXml = new SignedXml(document); signedXml.SigningKey = Key; Signature signature = signedXml.Signature; signature.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; Reference reference = new Reference("#" + authnRequest.ID); XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(env); XmlDsigExcC14NTransform excC14NTransform = new XmlDsigExcC14NTransform("ds saml samlp"); reference.AddTransform(excC14NTransform); signature.SignedInfo.AddReference(reference); signedXml.ComputeSignature(); XmlElement xmlDigitalSignature = signedXml.GetXml(); document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature, true)); result = document.OuterXml; Console.WriteLine("result: " + result); XmlTextWriter xmltw = new XmlTextWriter(TestConstants.workDir + "\\test.xml", new UTF8Encoding(false)); document.WriteTo(xmltw); xmltw.Close(); }
public SamlBodyRequest GetSamlRequest(Dictionary <CCAtributes, bool> CCRequestAttrs) { #region SAML initial request configs AuthnRequestType _request = new AuthnRequestType(); // saml-core-2.0-os - 3.2.1 // An identifier for the request. It is of type xs:ID and MUST follow the requirements specified in Section // 1.3.4 for identifier uniqueness. The values of the ID attribute in a request and the InResponseTo // attribute in the corresponding response MUST match _request.ID = "_" + Guid.NewGuid().ToString(); // saml-core-2.0-os - 3.2.1 // The version of this request. The identifier for the version of SAML defined in this specification is "2.0". // SAML versioning is discussed in Section 4. _request.Version = "2.0"; // saml-core-2.0-os - 3.2.1 // The time instant of issue of the request. The time value is encoded in UTC, as described in Section // 1.3.3. _request.IssueInstant = DateTime.UtcNow; // saml-core-2.0-os - 3.2.1 // A URI reference indicating the address to which this request has been sent. This is useful to prevent // malicious forwarding of requests to unintended recipients, a protection that is required by some // protocol bindings. If it is present, the actual recipient MUST check that the URI reference identifies the // location at which the message was received. If it does not, the request MUST be discarded. Some // protocol bindings may require the use of this attribute (see [SAMLBind]). _request.Destination = appSettings.Get("AuthGovPT.Saml.Request.Destination.Url"); // saml-core-2.0-os - 3.2.1 // Indicates whether or not (and under what conditions) consent has been obtained from a principal in // the sending of this request. See Section 8.4 for some URI references that MAY be used as the value // of the Consent attribute and their associated descriptions. If no Consent value is provided, the // identifier urn:oasis:names:tc:SAML:2.0:consent:unspecified (see Section 8.4.1) is in // effect. _request.Consent = "urn:oasis:names:tc:SAML:2.0:consent:unspecified"; // saml-core-2.0-os - 3.4.1 // A URI reference that identifies a SAML protocol binding to be used when returning the <Response> // message. See [SAMLBind] for more information about protocol bindings and URI references defined // for them. This attribute is mutually exclusive with the AssertionConsumerServiceIndex attribute // and is typically accompanied by the AssertionConsumerServiceURL attribute. _request.ProtocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"; // saml-core-2.0-os - 3.4.1 // Specifies by value the location to which the <Response> message MUST be returned to the // requester. The responder MUST ensure by some means that the value specified is in fact associated // with the requester. [SAMLMeta] provides one possible mechanism; signing the enclosing // <AuthnRequest> message is another. This attribute is mutually exclusive with the // AssertionConsumerServiceIndex attribute and is typically accompanied by the // ProtocolBinding attribute. _request.AssertionConsumerServiceURL = appSettings.Get("AuthGovPT.Saml.Request.AssertionService.Url"); // saml-core-2.0-os - 3.4.1 // Specifies the human-readable name of the requester for use by the presenter's user agent or the // identity provider. _request.ProviderName = appSettings.Get("AuthGovPT.Saml.Request.Provider.Name"); // saml-core-2.0-os - 2.2.5 // The <Issuer> element, with complex type NameIDType, provides information about the issuer of a // SAML assertion or protocol message. The element requires the use of a string to carry the issuer's name, // but permits various pieces of descriptive data (see Section 2.2.2). _request.Issuer = new NameIDType(); _request.Issuer.Value = appSettings.Get("AuthGovPT.Saml.Request.Issuer.Value"); // saml-core-2.0-os - 3.2.1 // This extension point contains optional protocol message extension elements that are agreed on // between the communicating parties. No extension schema is required in order to make use of this // extension point, and even if one is provided, the lax validation setting does not impose a requirement // for the extension to be valid. SAML extension elements MUST be namespace-qualified in a non- // SAML-defined namespace. _request.Extensions = new ExtensionsType(); #endregion #region Load Cartão de Cidadão Attributes _request.Extensions.Any = CCAttributes.RegisterCCAtributes(CCRequestAttrs, EnableAuthWithCMD); #endregion #region SAML Xml convert to stream XmlDocument doc = null; // Converter objeto para XmlDocument via stream usando serialização com os tipos AuthnRequestType e XmlDocument // http://support.microsoft.com/kb/815813/en-us try { MemoryStream stream = new MemoryStream(); XmlSerializer requestSerializer = new XmlSerializer(_request.GetType()); requestSerializer.Serialize(stream, _request, xmlNamespaces); stream.Flush(); StreamReader reader = new StreamReader(stream); stream.Seek(0, SeekOrigin.Begin); XmlTextReader xmlReader = new XmlTextReader(new StringReader(reader.ReadToEnd())); XmlSerializer xmlDocumentSerializer = new XmlSerializer(typeof(XmlDocument)); doc = (XmlDocument)xmlDocumentSerializer.Deserialize(xmlReader); doc.PreserveWhitespace = true; } catch (Exception ex) { //log SamlBodyRequest.Success = false; SamlBodyRequest.ErrorMessage = $"Error on XmlDocument object convertion. EX: {ex.ToString()} "; } #endregion #region SAML Xml Signning try { XmlElement element = doc.DocumentElement; SignedXml signedXml = new SignedXml(element) { SigningKey = FaX509Certificate.PrivateKey }; // Tipo de dados "ID" é restrito às strings em NCName: //<xs:simpleType name="ID" id="ID"> // <xs:annotation> // <xs:documentation source="http://www.w3.org/TR/xmlschema-2/#ID"/> // </xs:annotation> // <xs:restriction base="xs:NCName"/> //</xs:simpleType> // NCName está definido em http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName como: // NCName ::= (Letter | '_') (NCNameChar)* Reference reference = new Reference("#" + element.Attributes["ID"].Value); // Vide 5.4.3 "Canonicalization Method" e 5.4.4 "Transforms" em // http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf reference.AddTransform(new XmlDsigEnvelopedSignatureTransform()); reference.AddTransform(new XmlDsigExcC14NTransform()); signedXml.AddReference(reference); signedXml.KeyInfo.AddClause(new KeyInfoX509Data(FaX509Certificate)); signedXml.ComputeSignature(); XmlElement xmlDigitalSignature = signedXml.GetXml(); // AuthnRequestType define a ordem dos elementos filhos na schema saml-schema-protocol-2.0.xsd: //<complexType name="RequestAbstractType" abstract="true"> // <sequence> // <element ref="saml:Issuer" minOccurs="0"/> // <element ref="ds:Signature" minOccurs="0"/> // <element ref="ds:Signature" minOccurs="0"/> // <element ref="samlp:Extensions" minOccurs="0"/> // </sequence> // ... //</complexType> XmlNode refNode = doc.GetElementsByTagName("Issuer", "urn:oasis:names:tc:SAML:2.0:assertion").Item(0); element.InsertAfter(xmlDigitalSignature, refNode); } catch (Exception ex) { //TODO:: log exception ex SamlBodyRequest.Success = false; SamlBodyRequest.ErrorMessage = $"Error on Xml signing process. EX: {ex.ToString()}"; } #endregion #region Return SAML Request into Auth.gov FA SamlBodyRequest.RelayState = RelayStateToBepersistedAcross; SamlBodyRequest.SAMLRequest = Convert.ToBase64String(Encoding.UTF8.GetBytes(doc.OuterXml)); SamlBodyRequest.PostRequestUrl = appSettings.Get("AuthGovPT.Saml.Request.Post.Url"); SamlBodyRequest.Success = true; return(SamlBodyRequest); // Vide 3.5.3 "RelayState" em // http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf // "...The value MUST NOT exceed 80 bytes in length and SHOULD be integrity protected by the entity // creating the message independent of any other protections that may or may not exist during message // transmission..." // Vide 3.5 "HTTP POST Binding" em // http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf #endregion }
/// <summary> /// /// </summary> /// <param name="UUID"></param> /// <param name="Destination"></param> /// <param name="ConsumerServiceURL"></param> /// <param name="certFile"></param> /// <param name="certPassword"></param> /// <param name="storeLocation"></param> /// <param name="storeName"></param> /// <param name="findType"></param> /// <param name="findValue"></param> /// <param name="signatureType"></param> /// <returns></returns> public static string BuildPostSamlRequest(string UUID, string Destination, string ConsumerServiceURL, int SecurityLevel, string certFile, string certPassword, StoreLocation storeLocation, StoreName storeName, X509FindType findType, object findValue, SigningHelper.SignatureType signatureType, string IdentityProvider, int Enviroment) { AuthnRequestType MyRequest = new AuthnRequestType { ID = UUID, Version = "2.0" }; DateTime now = DateTime.UtcNow; DateTime after = now.AddMinutes(10); string nowString = String.Empty; string afterString = String.Empty; if (IdentityProvider.Contains("sielte")) { // SIELTE nowString = now.AddMinutes(-2).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"); afterString = after.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"); } else { // POSTE - TIM - INFOCERT nowString = now.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"); afterString = after.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"); } MyRequest.IssueInstant = nowString; if (SecurityLevel > 1) { MyRequest.ForceAuthn = true; MyRequest.ForceAuthnSpecified = true; } MyRequest.Destination = Destination; MyRequest.AssertionConsumerServiceIndex = (ushort)Enviroment; MyRequest.AssertionConsumerServiceIndexSpecified = true; MyRequest.AttributeConsumingServiceIndex = 1; MyRequest.AttributeConsumingServiceIndexSpecified = true; NameIDType IssuerForRequest = new NameIDType { Value = ConsumerServiceURL.Trim(), Format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", NameQualifier = ConsumerServiceURL }; MyRequest.Issuer = IssuerForRequest; NameIDPolicyType NameIdPolicyForRequest = new NameIDPolicyType { Format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", AllowCreate = true, AllowCreateSpecified = true }; MyRequest.NameIDPolicy = NameIdPolicyForRequest; ConditionsType Conditional = new ConditionsType(); if (IdentityProvider.Contains("sielte")) { // SIELTE Conditional.NotBefore = nowString; } else { // POSTE - TIM - INFOCERT Conditional.NotBefore = now.AddMinutes(-2).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'"); } Conditional.NotBeforeSpecified = true; Conditional.NotOnOrAfter = afterString; Conditional.NotOnOrAfterSpecified = true; MyRequest.Conditions = Conditional; RequestedAuthnContextType RequestedAuthn = new RequestedAuthnContextType { Comparison = AuthnContextComparisonType.minimum, ComparisonSpecified = true, ItemsElementName = new ItemsChoiceType7[] { ItemsChoiceType7.AuthnContextClassRef }, Items = new string[] { "https://www.spid.gov.it/SpidL" + SecurityLevel.ToString() } }; MyRequest.RequestedAuthnContext = RequestedAuthn; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("saml2p", "urn:oasis:names:tc:SAML:2.0:protocol"); //ns.Add("saml2", "urn:oasis:names:tc:SAML:2.0:assertion"); XmlSerializer responseSerializer = new XmlSerializer(MyRequest.GetType()); StringWriter stringWriter = new StringWriter(); XmlWriterSettings settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, Encoding = Encoding.UTF8 }; XmlWriter responseWriter = XmlTextWriter.Create(stringWriter, settings); responseSerializer.Serialize(responseWriter, MyRequest, ns); responseWriter.Close(); string samlString = string.Empty; samlString = stringWriter.ToString(); stringWriter.Close(); XmlDocument doc = new XmlDocument(); doc.LoadXml(samlString); X509Certificate2 cert = null; if (System.IO.File.Exists(certFile)) { cert = new X509Certificate2(certFile, certPassword); } else { X509Store store = new X509Store(storeName, storeLocation); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection CertCol = store.Certificates; X509Certificate2Collection coll = store.Certificates.Find(findType, findValue.ToString(), false); if (coll.Count < 1) { throw new ArgumentException("Unable to locate certificate"); } cert = coll[0]; store.Close(); } XmlElement signature = SigningHelper.SignDoc(doc, cert, UUID); doc.DocumentElement.InsertBefore(signature, doc.DocumentElement.ChildNodes[1]); string responseStr = doc.OuterXml; //byte[] base64EncodedBytes = // Encoding.UTF8.GetBytes(responseStr); //string returnValue = System.Convert.ToBase64String( // base64EncodedBytes); return("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + responseStr); }
protected virtual NameIDPolicyValidationResult CheckNameIDPolicy(RelyingPartyAggregate relyingParty, User user, AuthnRequestType authnRequest) { var attributes = ConvertAttributes(relyingParty, user); string nameIdFormat = Saml.Constants.NameIdentifierFormats.EntityIdentifier; string nameIdValue = user.Claims.First(c => c.Type == Jwt.Constants.UserClaims.Subject).Value; if (authnRequest.NameIDPolicy != null && (string.IsNullOrWhiteSpace(authnRequest.NameIDPolicy.Format) || authnRequest.NameIDPolicy.Format == Saml.Constants.NameIdentifierFormats.Unspecified)) { var attr = attributes.FirstOrDefault(a => a.AttributeFormat == authnRequest.NameIDPolicy.Format); if (attr == null) { throw new SamlException(HttpStatusCode.BadRequest, Saml.Constants.StatusCodes.InvalidNameIDPolicy, string.Format(Global.UnknownNameId, authnRequest.NameIDPolicy.SPNameQualifier)); } nameIdFormat = attr.AttributeFormat; nameIdValue = attr.Value; } return(new NameIDPolicyValidationResult { NameIdFormat = nameIdFormat, NameIdValue = nameIdValue, Attributes = attributes }); }
private async Task <HandleRequestResult> ValidateAuthenticationResponse(ResponseType response, AuthnRequestType request, AuthenticationProperties properties, string idPName) { if (response == null) { if (Options.SkipUnrecognizedRequests) { return(HandleRequestResult.SkipHandler()); } return(HandleRequestResult.Fail("No message.")); } if (properties == null && !Options.AllowUnsolicitedLogins) { return(HandleRequestResult.Fail("Unsolicited logins are not allowed.")); } var idp = Options.IdentityProviders.FirstOrDefault(x => x.Name == idPName); var metadataIdp = await DownloadMetadataIDP(idp.OrganizationUrlMetadata); response.ValidateAuthnResponse(request, metadataIdp); return(null); }
public async Task <(bool, AuthnRequestType)> HandleRedirectToIdentityProviderForAuthentication(HttpContext context, AuthenticationScheme scheme, SpidOptions options, AuthenticationProperties properties, AuthnRequestType message) { var redirectContext = new RedirectContext(context, scheme, options, properties, message); await _events.RedirectToIdentityProvider(redirectContext); return(redirectContext.Handled, (AuthnRequestType)redirectContext.SignedProtocolMessage); }
/// <summary> /// Build a signed SAML authentication request. /// </summary> /// <param name="uuid"></param> /// <param name="destination"></param> /// <param name="consumerServiceURL"></param> /// <param name="securityLevel"></param> /// <param name="certFile"></param> /// <param name="certPassword"></param> /// <param name="storeLocation"></param> /// <param name="storeName"></param> /// <param name="findType"></param> /// <param name="findValue"></param> /// <param name="identityProvider"></param> /// <param name="enviroment"></param> /// <returns>Returns a Base64 Encoded String of the SAML request</returns> public static string BuildAuthnPostRequest(string uuid, string destination, string consumerServiceURL, int securityLevel, X509Certificate2 certificate, IdentityProvider identityProvider, int enviroment) { if (string.IsNullOrWhiteSpace(uuid)) { throw new ArgumentNullException("The uuid parameter can't be null or empty."); } if (string.IsNullOrWhiteSpace(destination)) { throw new ArgumentNullException("The destination parameter can't be null or empty."); } if (string.IsNullOrWhiteSpace(consumerServiceURL)) { throw new ArgumentNullException("The consumerServiceURL parameter can't be null or empty."); } if (certificate == null) { throw new ArgumentNullException("The certificate parameter can't be null."); } if (identityProvider == null) { throw new ArgumentNullException("The identityProvider parameter can't be null."); } if (enviroment < 0) { throw new ArgumentNullException("The enviroment parameter can't be less than zero."); } DateTime now = DateTime.UtcNow; AuthnRequestType authnRequest = new AuthnRequestType { ID = "_" + uuid, Version = "2.0", IssueInstant = identityProvider.Now(now), Destination = destination, AssertionConsumerServiceIndex = (ushort)enviroment, AssertionConsumerServiceIndexSpecified = true, AttributeConsumingServiceIndex = 1, AttributeConsumingServiceIndexSpecified = true, ForceAuthn = (securityLevel > 1), ForceAuthnSpecified = (securityLevel > 1), Issuer = new NameIDType { Value = consumerServiceURL.Trim(), Format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", NameQualifier = consumerServiceURL }, NameIDPolicy = new NameIDPolicyType { Format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" }, Conditions = new ConditionsType { NotBefore = identityProvider.NotBefore(now), NotBeforeSpecified = true, NotOnOrAfter = identityProvider.After(now.AddMinutes(10)), NotOnOrAfterSpecified = true }, RequestedAuthnContext = new RequestedAuthnContextType { Comparison = AuthnContextComparisonType.minimum, ComparisonSpecified = true, ItemsElementName = new ItemsChoiceType7[] { ItemsChoiceType7.AuthnContextClassRef }, Items = new string[] { "https://www.spid.gov.it/SpidL" + securityLevel.ToString() } } }; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("saml2p", "urn:oasis:names:tc:SAML:2.0:protocol"); ns.Add("saml2", "urn:oasis:names:tc:SAML:2.0:assertion"); StringWriter stringWriter = new StringWriter(); XmlWriterSettings settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, Encoding = Encoding.UTF8 }; XmlWriter responseWriter = XmlTextWriter.Create(stringWriter, settings); XmlSerializer responseSerializer = new XmlSerializer(authnRequest.GetType()); responseSerializer.Serialize(responseWriter, authnRequest, ns); responseWriter.Close(); string samlString = stringWriter.ToString(); stringWriter.Close(); XmlDocument doc = new XmlDocument(); doc.LoadXml(samlString); XmlElement signature = XmlSigningHelper.SignXMLDoc(doc, certificate, "_" + uuid); doc.DocumentElement.InsertBefore(signature, doc.DocumentElement.ChildNodes[1]); return(Convert.ToBase64String(Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + doc.OuterXml))); }
/// <summary> /// Gets the authentication request. /// </summary> /// <returns></returns> public string GetAuthRequest() { string result = ""; DateTime requestDatTime = DateTime.UtcNow; //New AuthnRequestType AuthnRequestType request = new AuthnRequestType(); request.Version = Options.Version; //Unique UUID request.ID = "_" + this.Options.UUID; //Request DateTime request.IssueInstant = requestDatTime; //Request Force Authn if ((int)Options.SPIDLevel > 1) { request.ForceAuthn = true; request.ForceAuthnSpecified = true; } else { request.ForceAuthn = false; request.ForceAuthnSpecified = true; } //SSO Destination URI request.Destination = this.Options.Destination; //Service Provider Assertion Consumer Service Index request.AssertionConsumerServiceIndex = this.Options.AssertionConsumerServiceIndex; request.AssertionConsumerServiceIndexSpecified = true; //Service Provider Attribute Consumer Service Index request.AttributeConsumingServiceIndex = this.Options.AttributeConsumingServiceIndex; request.AttributeConsumingServiceIndexSpecified = true; //Service Provider Attribute Consumer Service Index request.AttributeConsumingServiceIndex = this.Options.AttributeConsumingServiceIndex; request.AttributeConsumingServiceIndexSpecified = true; //Issuer Data request.Issuer = new NameIDType() { Format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity", Value = Options.SPUID, NameQualifier = Options.SPUID }; request.NameIDPolicy = new NameIDPolicyType() { Format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", AllowCreate = true }; //NotRequired request.Conditions = new ConditionsType() { NotBefore = requestDatTime.Add(this.Options.NotBefore), NotBeforeSpecified = true, NotOnOrAfter = requestDatTime.Add(this.Options.NotOnOrAfter), NotOnOrAfterSpecified = true }; RequestedAuthnContextType requestedAuthn = new RequestedAuthnContextType { Comparison = AuthnContextComparisonType.minimum, ComparisonSpecified = true, ItemsElementName = new ItemsChoiceType7[] { ItemsChoiceType7.AuthnContextClassRef }, Items = new string[] { "https://www.spid.gov.it/SpidL" + ((int)Options.SPIDLevel).ToString() } }; request.RequestedAuthnContext = requestedAuthn; string samlString = ""; XmlSerializer serializer = new XmlSerializer(request.GetType()); using (StringWriter stringWriter = new StringWriter()) { XmlWriterSettings settings = new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true, Encoding = Encoding.UTF8 }; using (XmlWriter writer = XmlWriter.Create(stringWriter, settings)) { XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces(); namespaces.Add("saml2p", "urn:oasis:names:tc:SAML:2.0:protocol"); serializer.Serialize(writer, request, namespaces); samlString = stringWriter.ToString(); } } result = samlString; return(result); }