Click here to Skip to main content
15,887,214 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi!
We have an important project in our company whose goal is to perform ON TRANSIT encryption between the client and the backend.
The type of encryption we use is RSA 4K.
The encryption on the client side is already done nicely.
Also for decoding we already have the classes responsible for this and they work fine.
Our code is written in c# and we reveal to the client a long list of web services.
Every web service is written in WCF architecture (old, yes, and we want to get out of it but that's the situation at the moment).
My question is this:
We have hundreds of APIs in the system.
I am looking for a generic engine that knows how to catch the payload,
decrypt it, and then when the API starts working, the payload that reaches it will already be decrypted.

Here is what I cam up with:

I created an attribute class called "Something" (strange name, just for now):

C#
namespace Journeys	
    public sealed class SomethingAttribute : Attribute, IServiceBehavior
    {
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;

                if (channelDispatcher != null)
                {
                    foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                    {
                        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
                    }
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }
    }


As you can see, SomethingAttribute is implementing CustomMessageInspector class:

C#
namespace Company.S2S.OnTransit
{
    public class CustomMessageInspector : IDispatchMessageInspector
    {
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            try
            {
                MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
                request = buffer.CreateMessage();

                var copy = buffer.CreateMessage();
                XmlDictionaryReader bodyReader = copy.GetReaderAtBodyContents();
                bodyReader.ReadStartElement("Binary");
                byte[] bodyBytes = bodyReader.ReadContentAsBase64();
                string messageBody = Encoding.UTF8.GetString(bodyBytes);
                Log.LogThis($"CustomMessageInspector: messageBody is: {messageBody}", eloglevel.debug);
                string decryptedPayload = DecryptPayload(messageBody);
                Log.LogThis($"CustomMessageInspector: dec payload is: {decryptedPayload}", eloglevel.debug);

                using (var memoryStream = new MemoryStream())
                {
                    var writer = new StreamWriter(memoryStream);
                    writer.Write(decryptedPayload);
                    writer.Flush();
                    memoryStream.Position = 0;

                    var byteArray = SerializeMemoryStreamToByteArray(memoryStream);

                    request = CreateNewMessageBinary(byteArray, request, request.Version);
                }

                return null;
            }
            catch (Exception ex)
            {
                Generic.LogException(ex, "AfterReceiveRequest");
                throw;
            }
        }

		private byte[] SerializeMemoryStreamToByteArray(MemoryStream memoryStream)
        {
            byte[] byteArray = new byte[memoryStream.Length];
            memoryStream.Read(byteArray, 0, byteArray.Length);
            return byteArray;
        }
		
		private Message CreateNewMessageBinary(byte[] byteArray, Message request, MessageVersion version)
        {
            Message newMessage = Message.CreateMessage(version, null, new BinaryReader(new MemoryStream(byteArray)));
            newMessage.Headers.CopyHeadersFrom(request.Headers);
            newMessage.Properties.CopyProperties(request.Properties);

            return newMessage;
        }

    }
}



Here is an example of one of our web service (all of them looks like this).
Please see that JourneysEngine is now implementing the [Something] attribute.
C#
namespace Journeys
{
    [ServiceContract(Namespace = "https://www.company.net")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
	[Something]    
    public class JourneysEngine 
    {
	#region REST APIs
		[WebInvoke(ResponseFormat = WebMessageFormat.Json, Method = "POST", UriTemplate = "/GetJourneys")]
        [OperationContract]
        public Stream GetJourneys(Stream stream)
        {
            //I need the stream here after decoding
        }
	}
}


In a nutsheel, before the code hits the API, it pass through CustomMessageInspector.AfterReceiveRequest.
There, I can grab the Message and decode it.
The problem is here, when I hit the API - I'm getting the following exception:

### Exception details - System.IO.IOException:
An exception has been thrown when reading the stream. --->
System.Runtime.Serialization.InvalidDataContractException:
Type 'System.IO.BinaryReader' cannot be serialized.
Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized
with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute.
See the Microsoft .NET Framework documentation for other supported types.
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type)
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle,
Type type) at System.Runtime.Serialization.DataContractSerializer.get_RootContract()
at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph,
DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph,
DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.WriteObject(XmlDictionaryWriter writer, Object graph)
at System.ServiceModel.Channels.XmlObjectSerializerBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.OnGetReaderAtBodyContents()
at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Read(Byte[] buffer, Int32 offset, Int32 count)
--- End of inner exception stack trace ---
at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.InternalCopyTo(Stream destination, Int32 bufferSize)
at CallVU.Utils.Extensions.StreamExtensions.Stringify(Stream stream, Boolean print)
at Journeys.JourneysEngine.GetJourneys(Stream stream)

The Stringify method is an extension method (who is working great and for a long time that just takes the Stream passes to the API and return it as a string:

C#
public static string Stringify(this Stream stream, bool print = false)
        {
            Log.LogThis($"Stringify Started", eloglevel.trace);

            if (stream == null)
            {
                Log.LogThis($"Stream is null, return string empty", eloglevel.warn);
                return "";
            }

            // Check if the stream can seek
            bool canSeek = stream.CanSeek;

            // If the stream can't seek, copy it to a new memory stream that can be read from the start.
            if (!canSeek)
            {
                Log.LogThis($"cant seek in stream, copying it.", eloglevel.trace);
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    stream.CopyTo(memoryStream);
                    memoryStream.Position = 0;
                    string response = Encoding.UTF8.GetString(memoryStream.ToArray());
                    if (print) Log.LogThis($"stream as string: ({response})", eloglevel.trace);
                    return response;
                }
            }

            // If the stream can seek, read it directly into a string.
            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8, true, 1024, true))
            {
                string result = reader.ReadToEnd();
                stream.Position = 0; // Return to the start of the stream

                if (print)
                {
                    Log.LogThis($"Stream is: {result}", eloglevel.trace);
                }
                else
                {
                    Log.LogThis($"First 10 chars of stream are: {result.CutByLength(10)}", eloglevel.trace);
                }

                return result;
            }
        }


Just in case, here is the start of the API:
C#
[WebInvoke(ResponseFormat = WebMessageFormat.Json, Method = "POST", UriTemplate = "/GetJourney")]
        [OperationContract]
        public Stream GetJourney(Stream stream)
        {
            (string caller, string myName) = Generic.InitMethod(setThreadName: true);
            try
            {                
                string json = stream.Stringify();
                NLogger.Instance.Info($"{myName} - Start with the following payload: {json}");
                GetJourneyModel model = JsonHelper.DeserializeJson<GetJourneyModel>(json);          
		}


What I have tried:

I tried to implement the above code from the web.config without any luck what so ever
Posted
Updated 12-Nov-23 2:34am
v2
Comments
Richard MacCutchan 12-Nov-23 4:27am    
"I tried to implement the above code from the web.config without any luck what so ever"
What exactly does that mean? Please use the Improve question link above, and add complete details of what is not working.
oronsultan 12-Nov-23 8:37am    
I updated mu question the best that I can do.
Richard MacCutchan 12-Nov-23 9:09am    
the error message is clear:
System.Runtime.Serialization.InvalidDataContractException:
Type 'System.IO.BinaryReader' cannot be serialized.
Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized

So you need to find out where the error is being generated, and correct is according to the message.
oronsultan 12-Nov-23 9:11am    
Ah?
[no name] 12-Nov-23 12:48pm    
When you start "transporting" between "frameworks", the "data contract serializer" is about the only way to "insure" compatibility; the "other" methods don't always "align" properly; unless you use a "standard framework" library for all operations.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900