private async Task <CollaborationProtocolProfile> FindProfile(ILogger logger, OutgoingMessage message) { var profile = await CollaborationProtocolRegistry.FindAgreementForCounterpartyAsync(logger, message.ToHerId).ConfigureAwait(false) ?? await CollaborationProtocolRegistry.FindProtocolForCounterpartyAsync(logger, message.ToHerId).ConfigureAwait(false); return(profile); }
/// <summary> /// Sends an outgoing message /// </summary> /// <param name="logger"></param> /// <param name="outgoingMessage">Information about the message to send</param> /// <param name="queueType">The type of queue that should be used</param> /// <param name="replyTo">An optional ReplyTo queue that should be used. Only relevant in synchronous messaging</param> /// <param name="correlationId">The correlation id to use when sending the message. Only relevant in synchronous messaging</param> /// <returns></returns> internal async Task Send(ILogger logger, OutgoingMessage outgoingMessage, QueueType queueType, string replyTo = null, string correlationId = null) { logger.LogDebug($"Start-ServiceBusCore::Send QueueType: {queueType} replyTo: {replyTo} correlationId: {correlationId}"); if (outgoingMessage == null) { throw new ArgumentNullException(nameof(outgoingMessage)); } if (string.IsNullOrEmpty(outgoingMessage.MessageId)) { throw new ArgumentNullException(nameof(outgoingMessage.MessageId)); } if (outgoingMessage.Payload == null) { throw new ArgumentNullException(nameof(outgoingMessage.Payload)); } var hasAgreement = true; // first we try and find an agreement var profile = await CollaborationProtocolRegistry.FindAgreementForCounterpartyAsync(logger, outgoingMessage.ToHerId).ConfigureAwait(false); if (profile == null) { hasAgreement = false; // if we don't have an agreement, we try to find the specific profile profile = await CollaborationProtocolRegistry.FindProtocolForCounterpartyAsync(logger, outgoingMessage.ToHerId).ConfigureAwait(false); } logger.LogDebug($"ServiceBusCore::Send - Start retrieving and valiating certificates - correlationId: {correlationId}"); var signature = Settings.SigningCertificate.Certificate; var encryption = profile.EncryptionCertificate; var validator = Core.DefaultCertificateValidator; var encryptionStatus = validator.Validate(encryption, X509KeyUsageFlags.DataEncipherment); var signatureStatus = validator.Validate(signature, X509KeyUsageFlags.NonRepudiation); // this is the other parties certificate that may be out of date, not something we can fix if (encryptionStatus != CertificateErrors.None) { if (Core.Settings.IgnoreCertificateErrorOnSend) { logger.LogError(EventIds.RemoteCertificate, "Remote encryption certificate is not valid"); } else { throw new MessagingException("Remote encryption certificate is not valid") { EventId = EventIds.RemoteCertificate }; } } // this is our certificate, something we can fix if (signatureStatus != CertificateErrors.None) { if (Core.Settings.IgnoreCertificateErrorOnSend) { logger.LogError(EventIds.LocalCertificate, "Locally installed signing certificate is not valid"); } else { throw new MessagingException("Locally installed signing certificate is not valid") { EventId = EventIds.LocalCertificate }; } } logger.LogDebug($"ServiceBusCore::Send - End retrieving and valiating certificates - correlationId: {correlationId}"); logger.LogDebug($"ServiceBusCore::Send - Start encrypting message - correlationId: {correlationId}"); var protection = Core.DefaultMessageProtection; var stream = protection.Protect(outgoingMessage.Payload, encryption, signature); logger.LogDebug($"ServiceBusCore::Send - End encrypting message - correlationId: {correlationId}"); logger.LogDebug($"ServiceBusCore::Send - Start Create and Initialize message - correlationId: {correlationId}"); var messagingMessage = FactoryPool.CreateMessage(logger, stream, outgoingMessage); if (queueType != QueueType.SynchronousReply) { messagingMessage.ReplyTo = replyTo ?? await ConstructQueueName(logger, Core.Settings.MyHerId, queueType).ConfigureAwait(false); } messagingMessage.ContentType = protection.ContentType; messagingMessage.MessageId = outgoingMessage.MessageId; // when we are replying to a synchronous message, we need to use the replyto of the original message messagingMessage.To = (queueType == QueueType.SynchronousReply) ? replyTo : await ConstructQueueName(logger, outgoingMessage.ToHerId, queueType).ConfigureAwait(false); messagingMessage.MessageFunction = outgoingMessage.MessageFunction; messagingMessage.CorrelationId = correlationId ?? outgoingMessage.MessageId; messagingMessage.TimeToLive = (queueType == QueueType.Asynchronous) ? Settings.Asynchronous.TimeToLive : Settings.Synchronous.TimeToLive; messagingMessage.ScheduledEnqueueTimeUtc = outgoingMessage.ScheduledSendTimeUtc; messagingMessage.FromHerId = Core.Settings.MyHerId; messagingMessage.ToHerId = outgoingMessage.ToHerId; messagingMessage.ApplicationTimestamp = DateTime.Now; if (hasAgreement) { messagingMessage.CpaId = profile.CpaId.ToString("D"); } logger.LogDebug($"ServiceBusCore::Send - End Create and Initialize message - correlationId: {correlationId}"); await Send(logger, messagingMessage, queueType, outgoingMessage.PersonalId, (LogPayload)?outgoingMessage.Payload : null).ConfigureAwait(false); logger.LogDebug($"End-ServiceBusCore::Send QueueType: {queueType} replyTo: {replyTo} correlationId: {correlationId}"); }
/// <summary> /// Sends an outgoing message /// </summary> /// <param name="logger"></param> /// <param name="outgoingMessage">Information about the message to send</param> /// <param name="queueType">The type of queue that should be used</param> /// <param name="replyTo">An optional ReplyTo queue that should be used. Only relevant in synchronous messaging</param> /// <param name="correlationId">The correlation id to use when sending the message. Only relevant in synchronous messaging</param> /// <returns></returns> internal async Task Send(ILogger logger, OutgoingMessage outgoingMessage, QueueType queueType, string replyTo = null, string correlationId = null) { if (outgoingMessage == null) { throw new ArgumentNullException(nameof(outgoingMessage)); } if (string.IsNullOrEmpty(outgoingMessage.MessageId)) { throw new ArgumentNullException(nameof(outgoingMessage.MessageId)); } if (outgoingMessage.Payload == null) { throw new ArgumentNullException(nameof(outgoingMessage.Payload)); } logger.LogStartSend(queueType, outgoingMessage.MessageFunction, Core.Settings.MyHerId, outgoingMessage.ToHerId, outgoingMessage.MessageId, outgoingMessage.PersonalId, outgoingMessage.Payload); var hasAgreement = true; // first we try and find an agreement var profile = await CollaborationProtocolRegistry.FindAgreementForCounterpartyAsync(logger, outgoingMessage.ToHerId).ConfigureAwait(false); if (profile == null) { hasAgreement = false; // if we don't have an agreement, we try to find the specific profile profile = await CollaborationProtocolRegistry.FindProtocolForCounterpartyAsync(logger, outgoingMessage.ToHerId).ConfigureAwait(false); } var contentType = Core.MessageProtection.ContentType; if (contentType.Equals(ContentType.SignedAndEnveloped, StringComparison.OrdinalIgnoreCase)) { var validator = Core.CertificateValidator; // Validate external part's encryption certificate logger.LogBeforeValidatingCertificate(outgoingMessage.MessageFunction, profile.EncryptionCertificate.Thumbprint, profile.EncryptionCertificate.Subject, "DataEncipherment", outgoingMessage.ToHerId, outgoingMessage.MessageId); var encryptionStatus = validator == null ? CertificateErrors.None : validator.Validate(profile.EncryptionCertificate, X509KeyUsageFlags.DataEncipherment); logger.LogAfterValidatingCertificate(outgoingMessage.MessageFunction, profile.EncryptionCertificate.Thumbprint, profile.EncryptionCertificate.Subject, "DataEncipherment", outgoingMessage.ToHerId, outgoingMessage.MessageId); logger.LogBeforeValidatingCertificate(outgoingMessage.MessageFunction, Core.MessageProtection.SigningCertificate.Thumbprint, Core.MessageProtection.SigningCertificate.Subject, "NonRepudiation", Core.Settings.MyHerId, outgoingMessage.MessageId); // Validate "our" own signature certificate var signatureStatus = validator == null ? CertificateErrors.None : validator.Validate(Core.MessageProtection.SigningCertificate, X509KeyUsageFlags.NonRepudiation); logger.LogAfterValidatingCertificate(outgoingMessage.MessageFunction, Core.MessageProtection.SigningCertificate.Thumbprint, Core.MessageProtection.SigningCertificate.Subject, "NonRepudiation", Core.Settings.MyHerId, outgoingMessage.MessageId); // this is the other parties certificate that may be out of date, not something we can fix if (encryptionStatus != CertificateErrors.None) { if (Core.Settings.IgnoreCertificateErrorOnSend) { logger.LogError(EventIds.RemoteCertificate, $"Remote encryption certificate {profile.EncryptionCertificate?.SerialNumber} for {outgoingMessage.ToHerId.ToString()} is not valid"); } else { throw new MessagingException($"Remote encryption certificate {profile.EncryptionCertificate?.SerialNumber} for {outgoingMessage.ToHerId.ToString()} is not valid") { EventId = EventIds.RemoteCertificate }; } } // this is our certificate, something we can fix if (signatureStatus != CertificateErrors.None) { if (Core.Settings.IgnoreCertificateErrorOnSend) { logger.LogError(EventIds.LocalCertificate, $"Locally installed signing certificate {Core.MessageProtection.SigningCertificate?.SerialNumber} is not valid.\nSerial Number: {Core.MessageProtection.SigningCertificate?.SerialNumber}\nThumbprint: {Core.MessageProtection.SigningCertificate?.Thumbprint}"); } else { throw new MessagingException($"Locally installed signing certificate {Core.MessageProtection.SigningCertificate?.SerialNumber} is not valid.\nSerial Number: {Core.MessageProtection.SigningCertificate?.SerialNumber}\nThumbprint: {Core.MessageProtection.SigningCertificate?.Thumbprint}") { EventId = EventIds.LocalCertificate }; } } } logger.LogBeforeEncryptingPayload(outgoingMessage.MessageFunction, profile.EncryptionCertificate.Thumbprint, profile.EncryptionCertificate.Subject, Core.Settings.MyHerId, outgoingMessage.ToHerId, outgoingMessage.MessageId); // Encrypt the payload var stream = Core.MessageProtection.Protect(outgoingMessage.Payload?.ToStream(), profile.EncryptionCertificate); logger.LogAfterEncryptingPayload(outgoingMessage.MessageFunction, profile.EncryptionCertificate.Thumbprint, profile.EncryptionCertificate.Subject, Core.Settings.MyHerId, outgoingMessage.ToHerId, outgoingMessage.MessageId); logger.LogBeforeFactoryPoolCreateMessage(outgoingMessage.MessageFunction, Core.Settings.MyHerId, outgoingMessage.ToHerId, outgoingMessage.MessageId); // Create an empty message var messagingMessage = FactoryPool.CreateMessage(logger, stream, outgoingMessage); logger.LogAfterFactoryPoolCreateMessage(outgoingMessage.MessageFunction, Core.Settings.MyHerId, outgoingMessage.ToHerId, outgoingMessage.MessageId); if (queueType != QueueType.SynchronousReply) { messagingMessage.ReplyTo = replyTo ?? await ConstructQueueName(logger, Core.Settings.MyHerId, queueType).ConfigureAwait(false); } messagingMessage.ContentType = Core.MessageProtection.ContentType; messagingMessage.MessageId = outgoingMessage.MessageId; // when we are replying to a synchronous message, we need to use the replyto of the original message messagingMessage.To = (queueType == QueueType.SynchronousReply) ? replyTo : await ConstructQueueName(logger, outgoingMessage.ToHerId, queueType).ConfigureAwait(false); messagingMessage.MessageFunction = outgoingMessage.MessageFunction; messagingMessage.CorrelationId = correlationId ?? outgoingMessage.MessageId; messagingMessage.TimeToLive = (queueType == QueueType.Asynchronous) ? Settings.Asynchronous.TimeToLive : Settings.Synchronous.TimeToLive; messagingMessage.ScheduledEnqueueTimeUtc = outgoingMessage.ScheduledSendTimeUtc; messagingMessage.FromHerId = Core.Settings.MyHerId; messagingMessage.ToHerId = outgoingMessage.ToHerId; messagingMessage.ApplicationTimestamp = DateTime.Now; if (hasAgreement) { messagingMessage.CpaId = profile.CpaId.ToString("D"); } await Send(logger, messagingMessage, queueType, outgoingMessage.PersonalId, (LogPayload)?outgoingMessage.Payload : null).ConfigureAwait(false); logger.LogEndSend(queueType, messagingMessage.MessageFunction, messagingMessage.FromHerId, messagingMessage.ToHerId, messagingMessage.MessageId, outgoingMessage.PersonalId); }