public void Saml2SubjectExtensions_ToXElement_SubjectConfirmationData() { var destination = new Uri("http://sp.example.com"); var inResponseTo = new Saml2Id("abc123"); var notOnOrAfter = DateTime.UtcNow.AddMinutes(2); var notBefore = DateTime.UtcNow; var saml2SubjectConfirmationData = new Saml2SubjectConfirmationData { NotOnOrAfter = notOnOrAfter, InResponseTo = inResponseTo, Recipient = destination, NotBefore = notBefore }; var confirmation = saml2SubjectConfirmationData.ToXElement(); confirmation.Attribute("NotOnOrAfter").Value.Should().Be(notOnOrAfter.ToSaml2DateTimeString()); confirmation.Attribute("NotBefore").Value.Should().Be(notBefore.ToSaml2DateTimeString()); confirmation.Attribute("Recipient").Value.Should().Be(destination.OriginalString); confirmation.Attribute("InResponseTo").Value.Should().Be(inResponseTo.Value); }
public void Saml2SubjectConfirmationData_AbsoluteClassReference_NoException() { var recipient = new Uri("http://resource", UriKind.Absolute); var subjectConfirmationData = new Saml2SubjectConfirmationData(); subjectConfirmationData.Recipient = recipient; }
protected override Saml2Subject CreateSubject(SecurityTokenDescriptor tokenDescriptor) { var subject = base.CreateSubject(tokenDescriptor); if (subject.NameId != null && subject.NameId.Format == null) { subject.NameId.Format = Saml2Constants.NameIdentifierFormats.Unspecified; } if (tokenDescriptor is RequestedSecurityTokenDescriptor requestedTokenDescriptor) { var keyInfo = CreateProofKeyInfo(requestedTokenDescriptor); if (keyInfo != null) { var data = new Saml2SubjectConfirmationData { }; data.KeyInfos.Add(keyInfo); var holderOfKey = new Saml2SubjectConfirmation(Saml2Constants.ConfirmationMethods.HolderOfKey, data); var bearer = subject.SubjectConfirmations.FirstOrDefault(c => c.Method == Saml2Constants.ConfirmationMethods.Bearer); if (bearer != null) { subject.SubjectConfirmations.Remove(bearer); } subject.SubjectConfirmations.Add(holderOfKey); } } return(subject); }
public void Saml2SubjectConfirmationData_RelativeFormat_ArgumentException() { var recipient = new Uri("recipient", UriKind.Relative); var subjectConfirmationData = new Saml2SubjectConfirmationData(); Assert.Throws <ArgumentException>(() => subjectConfirmationData.Recipient = recipient); }
/// <summary> /// Writes out the subject confirmation data as an XElement. /// </summary> /// <param name="subjectConfirmationData"></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> public static XElement ToXElement(this Saml2SubjectConfirmationData subjectConfirmationData) { if (subjectConfirmationData == null) { throw new ArgumentNullException(nameof(subjectConfirmationData)); } var element = new XElement(Saml2Namespaces.Saml2 + "SubjectConfirmationData"); if (subjectConfirmationData.NotOnOrAfter.HasValue) { element.SetAttributeValue("NotOnOrAfter", subjectConfirmationData.NotOnOrAfter.Value.ToSaml2DateTimeString()); } if (subjectConfirmationData.InResponseTo != null) { element.SetAttributeValue("InResponseTo", subjectConfirmationData.InResponseTo.Value); } if (subjectConfirmationData.Recipient != null) { element.SetAttributeValue("Recipient", subjectConfirmationData.Recipient.OriginalString); } if (subjectConfirmationData.NotBefore.HasValue) { element.SetAttributeValue("NotBefore", subjectConfirmationData.NotBefore.Value.ToSaml2DateTimeString()); } return(element); }
protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData) { /* Saml2SecurityToken cannot be created from the Saml2Assertion because it contains a SubjectConfirmationData * which specifies an InResponseTo value. Enforcement of this value is not supported by default. * To customize SubjectConfirmationData processing, extend Saml2SecurityTokenHandler and override ValidateConfirmationData. */ // base.ValidateConfirmationData(confirmationData); }
public void Saml2SubjectExtensions_ToXElement_SubjectConfirmationData_CheckNull() { Saml2SubjectConfirmationData saml2SubjectConfirmationData = null; Action a = () => saml2SubjectConfirmationData.ToXElement(); a.ShouldThrow <ArgumentNullException>().And.ParamName.Should().Be("subjectConfirmationData"); }
private Saml2Assertion CreateSamlAssertion(SecurityKey signatureKey, SecurityKeyIdentifier signatureKeyIdentifier, SecurityKeyIdentifier proofKeyIdentifier) { Claim claim = base.ClientCredentials.Claims.FirstOrDefault((Claim c) => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"); if (claim == null) { throw new ArgumentException("At least one NameIdentifier/Identity claim must be present in the claimset", "ClaimsClientCredentials.Claims"); } Saml2Subject saml2Subject = new Saml2Subject { NameId = new Saml2NameIdentifier(claim.Value, new Uri("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")) }; Saml2SubjectConfirmationData saml2SubjectConfirmationData = new Saml2SubjectConfirmationData(); saml2SubjectConfirmationData.KeyIdentifiers.Add(proofKeyIdentifier); saml2Subject.SubjectConfirmations.Add(new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"), saml2SubjectConfirmationData)); IEnumerable <Saml2Attribute> enumerable = from c in base.ClientCredentials.Claims.Except(new Claim[] { claim }) select new Saml2Attribute(c.Type, c.Value); Claim claim2 = base.ClientCredentials.Claims.FirstOrDefault((Claim c) => c.Type == "http://www.tridion.com/2009/08/directoryservice/claims/directoryServiceName"); Saml2NameIdentifier issuer; if (claim2 == null) { Saml2Attribute saml2Attribute = new Saml2Attribute("http://www.tridion.com/2009/08/directoryservice/claims/directoryServiceName", claim.Issuer); enumerable = enumerable.Concat(new Saml2Attribute[] { saml2Attribute }); issuer = new Saml2NameIdentifier(claim.Issuer); } else { issuer = new Saml2NameIdentifier(claim2.Value); } DateTime utcNow = DateTime.UtcNow; Saml2Assertion saml2Assertion = new Saml2Assertion(issuer) { Subject = saml2Subject, IssueInstant = utcNow, Conditions = new Saml2Conditions { NotBefore = new DateTime?(utcNow), NotOnOrAfter = new DateTime?(utcNow + base.TokenValidityTimeSpan) }, Advice = new Saml2Advice(), SigningCredentials = new SigningCredentials(signatureKey, base.SecurityAlgorithmSuite.DefaultAsymmetricSignatureAlgorithm, base.SecurityAlgorithmSuite.DefaultDigestAlgorithm, signatureKeyIdentifier) }; saml2Assertion.Statements.Add(new Saml2AttributeStatement(enumerable)); if (base.ClientCredentials.AudienceUris != null) { saml2Assertion.Conditions.AudienceRestrictions.Add(new Saml2AudienceRestriction(base.ClientCredentials.AudienceUris)); } return(saml2Assertion); }
protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData) { if (confirmationData == null) { throw new ArgumentNullException("confirmationData"); } if (confirmationData.Address != null) { throw new NotSupportedException("Token 'Address' confirmation is not currently supported"); } if (confirmationData.InResponseTo != null) //ignore in response to (this is present when issuing an authen request but would require persisting state to track) { //throw new SecurityTokenException("ID4154: confirmationData.InResponseTo not supperted"); } if (null != confirmationData.Recipient && _subjectRecipientValidationMode != SubjectRecipientValidationMode.None) { switch (_subjectRecipientValidationMode) { case SubjectRecipientValidationMode.ExactMatch: if (HttpContext.Current.Request.Url != confirmationData.Recipient) { //handle the special case where we redirected from the old oauth.ashx patterns.. if (HttpContext.Current.Request.Url.ToString().Replace("samlresponse", "oauth.ashx").ToLower() != confirmationData.Recipient.ToString().ToLower()) { throw new ArgumentException(string.Format("Token 'Recipient' value {0} does not match the current URL requirement of {1}", confirmationData.Recipient, HttpContext.Current.Request.Url)); } } break; case SubjectRecipientValidationMode.HostAndScheme: if (HttpContext.Current.Request.Url.Host != confirmationData.Recipient.Host || HttpContext.Current.Request.Url.Scheme != confirmationData.Recipient.Scheme) { throw new ArgumentException(string.Format("Token 'Recipient' value {0} does not match the current host and or scheme requirement of {1}", confirmationData.Recipient, HttpContext.Current.Request.Url)); } break; case SubjectRecipientValidationMode.HostOnly: if (HttpContext.Current.Request.Url.Host != confirmationData.Recipient.Host) { throw new ArgumentException(string.Format("Token 'Recipient' value {0} does not match the current host requirement of {1}", confirmationData.Recipient, HttpContext.Current.Request.Url)); } break; } } DateTime utcNow = DateTime.UtcNow; if (confirmationData.NotBefore.HasValue && (AddTimespan(utcNow, base.Configuration.MaxClockSkew) < confirmationData.NotBefore.Value)) { throw new ArgumentOutOfRangeException(string.Format("The token is not valid before {0} and the current time is {1}", confirmationData.NotBefore.Value, utcNow)); } if (confirmationData.NotOnOrAfter.HasValue && (AddTimespan(utcNow, base.Configuration.MaxClockSkew.Negate()) >= confirmationData.NotOnOrAfter.Value)) { throw new ArgumentOutOfRangeException(string.Format("The token is not valid after {0} and the current time is {1}", confirmationData.NotOnOrAfter.Value, utcNow)); } }
private void AddSubjectConfirmationData(int subjectConfirmationLifetime) { var subjectConfirmationData = new Saml2SubjectConfirmationData { Recipient = Destination.Uri, NotOnOrAfter = DateTime.UtcNow.AddMinutes(subjectConfirmationLifetime), }; Saml2SecurityToken.Assertion.Subject.SubjectConfirmations.Clear(); Saml2SecurityToken.Assertion.Subject.SubjectConfirmations.Add(new Saml2SubjectConfirmation(Saml2Constants.Saml2BearerToken, subjectConfirmationData)); }
public void InvokeClausValidatorTest() { //ARRANGE var result = false; var validator = new SubjectConfirmationDataValidatorMock(() => result = true); var invoker = new ValidatorInvoker(t => validator); var arg = new Saml2SubjectConfirmationData(); //ACT invoker.Validate(arg); //ASSERT Assert.IsTrue(result); }
protected override Saml2SubjectConfirmationData ReadSubjectConfirmationData(XmlReader reader) { var result = new Saml2SubjectConfirmationData(); if (!reader.IsStartElement("SubjectConfirmationData", "urn:oasis:names:tc:SAML:2.0:assertion")) { reader.ReadStartElement("SubjectConfirmationData", "urn:oasis:names:tc:SAML:2.0:assertion"); } string attribute2 = reader.GetAttribute("InResponseTo"); result.InResponseTo = new Saml2Id("test"); reader.Read(); reader.ReadEndElement(); return(result); return(base.ReadSubjectConfirmationData(reader)); }
protected virtual Saml2SubjectConfirmation CreateSubjectConfirmation(int subjectConfirmationLifetime) { if (Destination == null) { throw new ArgumentNullException("Destination property"); } var subjectConfirmationData = new Saml2SubjectConfirmationData { Recipient = Destination, NotOnOrAfter = DateTime.UtcNow.AddMinutes(subjectConfirmationLifetime), }; if (InResponseTo != null) { subjectConfirmationData.InResponseTo = InResponseTo; } return(new Saml2SubjectConfirmation(Saml2Constants.Saml2BearerToken, subjectConfirmationData)); }
public Saml2SubjectConfirmation CreateSubjectConfirmation(DateTimeOffset tokenIssueTime, int subjectConfirmationLifetime) { if (Destination == null) { throw new ArgumentNullException("Destination property"); } var subjectConfirmationData = new Saml2SubjectConfirmationData { Recipient = Destination, NotOnOrAfter = tokenIssueTime.AddSeconds(subjectConfirmationLifetime).UtcDateTime, }; if (InResponseTo != null) { subjectConfirmationData.InResponseTo = InResponseTo; } return(new Saml2SubjectConfirmation(Schemas.Saml2Constants.Saml2BearerToken, subjectConfirmationData)); }
private static Saml2Assertion CreateAssertion(SigningCredentials samlpTokenSigningCredentials) { var assertion = new Saml2Assertion(new Saml2NameIdentifier("https://mojeid.regtest.nic.cz/saml/idp.xml", new Uri("urn:oasis:names:tc:SAML:2.0:nameid-format:entity"))) { InclusiveNamespacesPrefixList = "ns1 ns2", IssueInstant = DateTime.Parse("2019-04-08T10:30:49Z"), SigningCredentials = samlpTokenSigningCredentials, Id = new Saml2Id("id-2bMsOPOKIqeVIDLqJ") }; var saml2SubjectConfirmationData = new Saml2SubjectConfirmationData { InResponseTo = new Saml2Id("ida5714d006fcc430c92aacf34ab30b166"), NotOnOrAfter = DateTime.Parse("2019-04-08T10:45:49Z"), Recipient = new Uri("https://tnia.eidentita.cz/fpsts/processRequest.aspx") }; var saml2SubjectConfirmation = new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:bearer"), saml2SubjectConfirmationData); var saml2Subject = new Saml2Subject(new Saml2NameIdentifier("6dfe0399103d11411b1fa00772b6a13e0858605b80c20ea845769c57b41479ed", new Uri("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"))); saml2Subject.SubjectConfirmations.Add(saml2SubjectConfirmation); assertion.Subject = saml2Subject; var saml2AudienceRestrictions = new Saml2AudienceRestriction("urn:microsoft: cgg2010: fpsts"); var saml2Conditions = new Saml2Conditions(); saml2Conditions.AudienceRestrictions.Add(saml2AudienceRestrictions); saml2Conditions.NotBefore = DateTime.Parse("2019-04-08T10:30:49Z"); saml2Conditions.NotOnOrAfter = DateTime.Parse("2019-04-08T10:45:49Z"); assertion.Conditions = saml2Conditions; var saml2AuthenticationContext = new Saml2AuthenticationContext(new Uri("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")); var saml2Statement = new Saml2AuthenticationStatement(saml2AuthenticationContext) { AuthenticationInstant = DateTime.Parse("2019-04-08T10:30:49Z"), SessionIndex = "id-oTnhrqWtcTTEntvMy" }; assertion.Statements.Add(saml2Statement); return(assertion); }
private async Task <Saml2SecurityToken> CreateSecurityTokenAsync(SignInRequest request, RelyingParty rp, ClaimsIdentity outgoingSubject) { var now = DateTime.Now; var outgoingNameId = outgoingSubject.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier); if (outgoingNameId == null) { _logger.LogError("The user profile does not have a name id"); throw new SignInException("The user profile does not have a name id"); } var issuer = new Saml2NameIdentifier(_options.IssuerName); var nameId = new Saml2NameIdentifier(outgoingNameId.Value); var subjectConfirmationData = new Saml2SubjectConfirmationData(); subjectConfirmationData.NotOnOrAfter = now.AddMinutes( rp.TokenLifetimeInMinutes.GetValueOrDefault(_options.DefaultNotOnOrAfterInMinutes)); if (request.Parameters.ContainsKey("Recipient")) { subjectConfirmationData.Recipient = new Uri(request.Parameters["Recipient"]); } else { subjectConfirmationData.Recipient = new Uri(rp.ReplyUrl); } var subjectConfirmation = new Saml2SubjectConfirmation(new Uri("urn:oasis:names:tc:SAML:2.0:cm:bearer"), subjectConfirmationData); subjectConfirmation.NameIdentifier = nameId; var subject = new Saml2Subject(subjectConfirmation); var conditions = new Saml2Conditions(new Saml2AudienceRestriction[] { new Saml2AudienceRestriction(request.Realm) }); conditions.NotOnOrAfter = now.AddMinutes( rp.TokenLifetimeInMinutes.GetValueOrDefault(_options.DefaultNotOnOrAfterInMinutes)); conditions.NotBefore = now.Subtract(TimeSpan.FromMinutes(_options.DefaultNotBeforeInMinutes)); var authContext = new Saml2AuthenticationContext(new Uri("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")); var authStatement = new Saml2AuthenticationStatement(authContext, now); authStatement.SessionIndex = (request.Parameters.ContainsKey("SessionIndex")) ? request.Parameters["SessionIndex"] : null; var attributeStament = new Saml2AttributeStatement(); foreach (var claim in outgoingSubject.Claims) { _logger.LogDebug("Adding attribute in SAML token '{0} - {1}'", claim.Type, claim.Value); attributeStament.Attributes.Add(new Saml2Attribute(claim.Type, claim.Value)); } var assertion = new Saml2Assertion(issuer); assertion.Id = new Saml2Id(); assertion.Subject = subject; assertion.Conditions = conditions; assertion.Statements.Add(attributeStament); assertion.Statements.Add(authStatement); assertion.IssueInstant = now; assertion.SigningCredentials = await _keyService.GetSigningCredentialsAsync(); var token = new Saml2SecurityToken(assertion); token.SigningKey = assertion.SigningCredentials.Key; return(token); }
/// <summary> /// Creates a Saml2Assertion from a ClaimsIdentity. /// </summary> /// <param name="identity">Claims to include in Assertion.</param> /// <param name="issuer">Issuer to include in assertion.</param> /// <param name="audience">Audience to set as audience restriction.</param> /// <param name="inResponseTo">In response to id</param> /// <param name="destinationUri">The destination Uri for the message</param> /// <returns>Saml2Assertion</returns> /// <exception cref="ArgumentNullException"></exception> public static Saml2Assertion ToSaml2Assertion( this ClaimsIdentity identity, EntityId issuer, Uri audience, Saml2Id inResponseTo, Uri destinationUri) { if (identity == null) { throw new ArgumentNullException(nameof(identity)); } if (issuer == null) { throw new ArgumentNullException(nameof(issuer)); } var assertion = new Saml2Assertion(new Saml2NameIdentifier(issuer.Id)); assertion.Statements.Add( new Saml2AuthenticationStatement( new Saml2AuthenticationContext( new Uri("urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"))) { SessionIndex = identity.Claims.SingleOrDefault( c => c.Type == Saml2ClaimTypes.SessionIndex)?.Value }); var attributeClaims = identity.Claims.Where( c => c.Type != ClaimTypes.NameIdentifier && c.Type != Saml2ClaimTypes.SessionIndex).GroupBy(c => c.Type) .ToArray(); if (attributeClaims.Any()) { assertion.Statements.Add( new Saml2AttributeStatement( attributeClaims.Select( ac => new Saml2Attribute(ac.Key, ac.Select(c => c.Value))))); } var notOnOrAfter = DateTime.UtcNow.AddMinutes(2); Saml2SubjectConfirmationData confirmationData = new Saml2SubjectConfirmationData { NotOnOrAfter = notOnOrAfter, InResponseTo = inResponseTo }; // Work around a bug in Microsoft.IdentityModel.Tokens.Saml2.Saml2SubjectConfirmationData // where the setter for Recipient throws an ArgumentNullException. Recipient is optional // as per [Saml2Core, 2.4.1.2] if (destinationUri != null) { confirmationData.Recipient = destinationUri; } assertion.Subject = new Saml2Subject(identity.ToSaml2NameIdentifier()) { SubjectConfirmations = { new Saml2SubjectConfirmation( new Uri("urn:oasis:names:tc:SAML:2.0:cm:bearer"), confirmationData) } }; assertion.Conditions = new Saml2Conditions() { NotOnOrAfter = notOnOrAfter }; if (audience != null) { assertion.Conditions.AudienceRestrictions.Add( new Saml2AudienceRestriction(audience.ToString())); } return(assertion); }
protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData) { confirmationData.Recipient = new Uri("http://foo"); // base.ValidateConfirmationData(confirmationData); }
protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData) { this._validatorInvoker.Validate(confirmationData); }
protected override void ValidateConfirmationData(Saml2SubjectConfirmationData confirmationData) { confirmationData.Recipient = new Uri("http://foo"); // base.ValidateConfirmationData(confirmationData); }
protected override Saml2SubjectConfirmationData ReadSubjectConfirmationData(XmlDictionaryReader reader) { XmlUtil.CheckReaderOnEntry(reader, Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Namespace); try { var confirmationData = new Saml2SubjectConfirmationData(); bool isEmpty = reader.IsEmptyElement; // @xsi:type bool requireKeyInfo = false; var type = XmlUtil.GetXsiTypeAsQualifiedName(reader); if (null != type) { if (XmlUtil.EqualsQName(type, Saml2Constants.Types.KeyInfoConfirmationDataType, Saml2Constants.Namespace) || type.Name == Saml2Constants.Types.KeyInfoConfirmationDataType) { requireKeyInfo = true; } else if (!XmlUtil.EqualsQName(type, Saml2Constants.Types.SubjectConfirmationDataType, Saml2Constants.Namespace) && type.Name != Saml2Constants.Types.SubjectConfirmationDataType) { throw XmlUtil.LogReadException(GetLogMessage("IDX13126"), type.Name, type.Namespace); } } // KeyInfoConfirmationData cannot be empty if (requireKeyInfo && isEmpty) { throw XmlUtil.LogReadException(GetLogMessage("IDX13127")); } // @Address - optional string value = reader.GetAttribute(Saml2Constants.Attributes.Address); if (!string.IsNullOrEmpty(value)) { confirmationData.Address = value; } // @InResponseTo - optional value = reader.GetAttribute(Saml2Constants.Attributes.InResponseTo); if (!string.IsNullOrEmpty(value)) { confirmationData.InResponseTo = new Saml2Id(value); } // @NotBefore - optional value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore); if (!string.IsNullOrEmpty(value)) { confirmationData.NotBefore = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc); } // @NotOnOrAfter - optional value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter); if (!string.IsNullOrEmpty(value)) { confirmationData.NotOnOrAfter = XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.Utc); } // @Recipient - optional value = reader.GetAttribute(Saml2Constants.Attributes.Recipient); if (!string.IsNullOrEmpty(value)) { if (!Uri.TryCreate(value, UriKind.Absolute, out _)) { throw XmlUtil.LogReadException(GetLogMessage("IDX13107"), Saml2Constants.Elements.SubjectConfirmationData, Saml2Constants.Attributes.Recipient, reader.LocalName); } confirmationData.Recipient = new Uri(value); } // Contents reader.Read(); if (!isEmpty) { while (reader.IsStartElement(XmlSignatureConstants.Elements.KeyInfo, XmlSignatureConstants.Namespace)) { confirmationData.KeyInfos.Add(DSigSerializer.ReadKeyInfo(reader)); } // If this isn't KeyInfo restricted, there might be open content here ... if (!requireKeyInfo && XmlNodeType.EndElement != reader.NodeType) { // So throw and tell the user how to handle the open content throw XmlUtil.LogReadException(GetLogMessage("IDX13128"), Saml2Constants.Elements.SubjectConfirmationData); } reader.ReadEndElement(); } return(confirmationData); } catch (Exception ex) { if (ex is Saml2SecurityTokenReadException) { throw; } throw XmlUtil.LogReadException(GetLogMessage("IDX13102"), ex, Saml2Constants.Elements.SubjectConfirmationData, ex); } }
public void Saml2SubjectConfirmationData_NullFormat_Noxception() { var subjectConfirmationData = new Saml2SubjectConfirmationData(); Assert.Throws <ArgumentNullException>(() => subjectConfirmationData.Recipient = null); }