How to securely transfer Web API data in ASP.NET Core?

How to securely transfer Web API data in ASP.NET Core?

tudip-logo

Tudip

09 July 2020

Nowadays, many applications are transferring sensitive data in a plain text format to and from other applications, which leads to data breach or leaks. We could prevent such data leaks by encrypting all the data before transfer it over the network. In this blog, we will see how to encrypt outgoing data and decrypt incoming data in ASP.NET Core Web APIs using the AES symmetric encryption algorithm.

Solution Overview

All the data transfers with the Client App will be encrypted. On the server-side, we would have an interceptor that would decrypt the incoming Request and encrypt the outgoing Response.

Transfer_Web_API_data_In_ASPNET_01

How to intercept the Request and Response data?

To intercept the incoming and outgoing data, we need to create custom middleware, “EncryptionMiddleware” to encrypt the outgoing Response data and decrypt the incoming Request data.

In the below code snippet, we have the decryption and encryption layer on the incoming Request body and outgoing Response body using DecryptStream and EncryptStream respectively. Also, we have DecryptString to decrypt the query string. We could customize the request pipeline to only execute data encryption on selective API routes. In the below configuration, we are encrypting all the data that is going out of our application.

public class EncryptionMiddleware
{
    private readonly RequestDelegate _next;

    public EncryptionMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        httpContext.Response.Body = EncryptStream(httpContext.Response.Body);
        httpContext.Request.Body = DecryptStream(httpContext.Request.Body);
        if (httpContext.Request.QueryString.HasValue)
        {
            string decryptedString = DecryptString(httpContext.Request.QueryString.Value.Substring(1));
            httpContext.Request.QueryString = new QueryString($"?{decryptedString}");
        }
        await _next(httpContext);
        await httpContext.Request.Body.DisposeAsync();
        await httpContext.Response.Body.DisposeAsync();
    }
}

How to encrypt the Response data?

To encrypt outgoing Response, we need to add two CryptoStream layers over the Response, first to encrypt the Response and second to transform the encrypted data in Base64 format.

In the below code snippet, we have “encryptedStream” and “base64EncodedStream” to encrypt and Base64 encode the Response data respectively.

private static CryptoStream EncryptStream(Stream responseStream)
{
    Aes aes = GetEncryptionAlgorithm();

    ToBase64Transform base64Transform = new ToBase64Transform();
    CryptoStream base64EncodedStream = new CryptoStream(responseStream, base64Transform, CryptoStreamMode.Write);
    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
    CryptoStream cryptoStream = new CryptoStream(base64EncodedStream, encryptor, CryptoStreamMode.Write);

    return cryptoStream;
}

private static Aes GetEncryptionAlgorithm()
{
    Aes aes = Aes.Create();
    aes.Key = secret_key;
    aes.IV = initialization_vector;

    return aes;
}

How to decrypt the Request data?

To decrypt the incoming Request, we need to add two CryptoStream layers, first to decode the Base64 Request data and second to decrypt the decoded data.

In the below code snippet, we have “base64DecodedStream” and “decryptedStream” to Base64 decode and decrypt the Request data respectively.

private static Stream DecryptStream(Stream cipherStream)
{
    Aes aes = GetEncryptionAlgorithm();

    FromBase64Transform base64Transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
    CryptoStream base64DecodedStream = new CryptoStream(cipherSteam, base64Transform, CryptoStreamMode.Read);
    ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
    CryptoStream decryptedStream = new CryptoStream(base64DecodedStream, decryptor, CryptoStreamMode.Read);
    return decryptedStream;
}

How to decrypt the query string?

Client app could encrypt query parameters in 2 ways,

  1. Encrypt the entire query string.
  2. Encrypt only the query parameter values.

In this example, we will consider the 1st case, i.e. the client app is sending the entire query string in an encrypted value. To decrypt the entire query string, we need to first decode the Base64 encoded query string and then to decrypt the decoded string to form the actual query string.

private static string DecryptString(string cipherText)
{
    Aes aes = GetEncryptionAlgorithm();
    byte[] buffer = Convert.FromBase64String(cipherText);

    using MemoryStream memoryStream = new MemoryStream(buffer);
    using ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
    using CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
    using StreamReader streamReader = new StreamReader(cryptoStream);
    return streamReader.ReadToEnd();
}

Where to place EncryptionMiddleware in our app’s pipeline?

We need to place EncryptionMiddleware before any action in the pipeline which writes a response. So that each response will be encrypted.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseMiddleware<EncryptionMiddleware>();
    *
    * //Removed the default pipeline setup for brevity
    *
}

Please refer the Microsoft documentation for writing custom middleware:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1

search
Blog Categories
Request a quote