Encrypt - Encrypt Payload Using Angular 11 And ASP.NET Core

Introduction

Nowadays, clients want to secure their websites. They want no one to be able to see the whole payload or id parameter value that we pass during an API call. Using the methods discussed here, you can get the exact solution.

In this article, I have explained everything that is more useful for security.

First, I am going to explain Encrypt and Decrypt whole payload or any id using Angular 11(front end) and ASP.NET CORE (API).

I have used AES256 algorithm in both Angular and ASP.NET CORE. in angular side using interceptor, passing the payload or id with encryption and using same algorithm, getting payload or ids in ASP.NET CORE using middleware that I will explain step by step.

Make sure that if we use any algorithm for encryption from angular or any other front-end framework, we must use the same algorithm for decryption.

Let me explain step by step, starting with Angular code.

First, you have to create a project. If not, create and do all processes like CRUD or any functionality that use GET and POST, else you can add to your current project also.

Angular Code

Step 1

Install the crypto js using the npm terminal. Use the below command to install the crypto js:

npm i crypto-js

I have used 4.1.1 version. You can use according to your angular version.

Once we've installed Crypto Js, we have to create one service to encrypt the payload or parameter.

Step 2

Create the service for Encrypt-Decrypt payload with the AES256 algorithm.

For Encrypt KEY and Encrypt IV, we have to obtain a unique 16-number code that we will use in encrypt, and also use same key and IV in asp.net code side for decrypt.

Here I am using same id for both. You can set them differently, but make sure to use the same key and IV to decrypt the code.

environment.EncryptKey = 1203199320052021

environment.EncryptIV = 1203199320052021

Here I have created a service and given it the name encrypt-decrypt.service.ts.

import { Injectable } from '@angular/core';
import * as CryptoJS from 'crypto-js';
import { environment } from 'src/environments/environment';

@Injectable({
    providedIn: 'root'
})
export class EncryptDecryptService {
    private key = CryptoJS.enc.Utf8.parse(environment.EncryptKey);
    private iv = CryptoJS.enc.Utf8.parse(environment.EncryptIV);
    constructor() {}
    // Methods for the encrypt and decrypt Using AES
    encryptUsingAES256(text): any {
        var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(text), this.key, {
            keySize: 128 / 8,
            iv: this.iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return encrypted.toString();
    }
    decryptUsingAES256(decString) {
        var decrypted = CryptoJS.AES.decrypt(decString, this.key, {
            keySize: 128 / 8,
            iv: this.iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return decrypted.toString(CryptoJS.enc.Utf8);
    }
}

So, the above code is for encrypting and decrypting the payload and parameter. For these functions we will use the interceptor method.

HTTP Interceptors is a special type of angular service that we can implement. It's used to apply custom logic to the central point between the client-side and server-side outgoing/incoming HTTP request and response. Keep in mind that the interceptor wants only HTTP requests.

Let’s create an interceptor.

Step 3

Create the interceptor file to generate encrypted payload before passing it into API service call.

I created interceptor with encrypt-decrypt-interceptor.ts name.

See the below code of interceptor method:

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { EncryptDecryptService } from './services/encrypt-decrypt.service';
import { Observable } from 'rxjs/Observable';
import { environment } from 'src/environments/environment';

@Injectable()
export class EncryptDecryptAuthInterceptor implements HttpInterceptor {
    constructor(private encryptDecryptService: EncryptDecryptService, ) {}
    // If you want to some exclude api call from Encryption then add here like that.
    // environment.basUrl is your API URL
    ExcludeURLList = [
        environment.baseUrl + "/api/Common/commonFileuploaddata",
        environment.baseUrl + "/api/Users/UploadProfilePicture",
        environment.baseUrl + "/api/Common/downloadattachedfile"
    ];
    intercept(req: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> {
        let exludeFound = this.ExcludeURLList.filter(element => {
            return req.url.includes(element)
        });
        // We have Encrypt the GET and POST call before pass payload to API
        if (!(exludeFound && exludeFound.length > 0)) {
            if (req.method == "GET") {
                if (req.url.indexOf("?") > 0) {
                    let encriptURL = req.url.substr(0, req.url.indexOf("?") + 1) + this.encryptDecryptService.encryptUsingAES256(req.url.substr(req.url.indexOf("?") + 1, req.url.length));
                    const cloneReq = req.clone({
                        url: encriptURL
                    });
                    return next.handle(cloneReq);
                }
                return next.handle(req);
            } else if (req.method == "POST") {
                if (req.body || req.body.length > 0) {
                    const cloneReq = req.clone({
                        body: this.encryptDecryptService.encryptUsingAES256(req.body)
                    });
                    return next.handle(cloneReq);
                }
                let data = req.body as FormData;
                return next.handle(req);
            }
        }
        return next.handle(req);
    }
}

I want to mention specifically that if you want to exclude API calls from encryption, you have to just add an API URL into ExcludeURLList as shown in Interceptor. Our project has many methods, or API calls, that are unnecessary to encrypt. Then, we can manage in our way. Suppose here I mention that some API calls exclude lists that upload images that are related. So for uploading images, encryption not needed. So, I added it under the exclude list.

Now need to configure it into a module file so that whenever we call the API it will call the interceptor before API and encrypt the payload. Then it will pass into API (GET or POST).

Step 4: Configure interceptor in app.module.ts file

First, import the inceptor that we created (encrypt-decrypt-interceptor.ts) and configure Interceptor into provider.

See the below sample app.module.ts:

import { NgModule } from '@angular/core'; 
import { BrowserModule } from '@angular/platform-browser';
import { EncryptDecryptAuthInterceptor } from './encrypt-decrypt-interceptor';
@NgModule({
    imports: [],
    declarations: [
        AppComponent,
    ],
    providers: [{
            provide: 'BASE_URL',
            useFactory: getBaseUrl
        }, {
            provide: ErrorHandler,
            useClass: AppErrorHandler
        }, {
            provide: HTTP_INTERCEPTORS,
            useClass: EncryptDecryptAuthInterceptor,
            multi: true
        },
        CommonService,
    ],
    bootstrap: [AppComponent],
    entryComponents: [],
    exports: [],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
export function getBaseUrl() {
    return document.getElementsByTagName('base')[0].href;
}

Add Interceptor into providers as shown in the above code.

Now, the front-end Angular side code is done using all the above steps to encrypt the payload and parameter so that no one can see the original payload or parameter.

See the above image to understand encryption. It shows a fully encrypted payload/parameter, so no one can see the original payload/parameter value.

ASP.NET Core Code

In Asp.Net core, we must use the same algorithm to decrypt the payload before passing to Action method.

So for that, I have created one middleware helper to decrypt the payload before calling the Controller Action method. The main benefit to this is that we have not written much code for everything. Just use middleware and call the middleware from the Startup.cs file to call middleware on every request of ASP.NET CORE.

We can set exclude list same as in Angular in ASP.Net Core.

Middleware is software components that are assembled into an application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline.

First, we need to create a middleware helper to encrypt-decrypt the payload and parameter, which is passing from angular.

Create a middleware helper name EncryptionMiddleware.cs and copy and paste the below code.

using FC_API.Data.Access;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Rajesh_API.Helpers {
    public class EncryptionMiddleware {
        private readonly RequestDelegate _next;
        private readonly AppSettings _appSettings;
        public EncryptionMiddleware(RequestDelegate next, IOptions < AppSettings > appSettings) {
            _next = next;
            _appSettings = appSettings.Value;
        }
        // Whenever we call any action method then call this before call the action method
        public async Task Invoke(HttpContext httpContext) {
            List < string > excludeURL = GetExcludeURLList();
            if (!excludeURL.Contains(httpContext.Request.Path.Value)) {
                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);
        }
        // This function is not needed but if we want anything to encrypt then we can use 
        private 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;
        }
        static byte[] Encrypt(string plainText) {
            byte[] encrypted;
            using(AesManaged aes = new AesManaged()) {
                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
                using(MemoryStream ms = new MemoryStream()) {
                    using(CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) {
                        using(StreamWriter sw = new StreamWriter(cs))
                        sw.Write(plainText);
                        encrypted = ms.ToArray();
                    }
                }
            }
            return encrypted;
        }
        // This are main functions that we decrypt the payload and  parameter which we pass from the angular service.
        private Stream DecryptStream(Stream cipherStream) {
            Aes aes = GetEncryptionAlgorithm();
            FromBase64Transform base64Transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
            CryptoStream base64DecodedStream = new CryptoStream(cipherStream, base64Transform, CryptoStreamMode.Read);
            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            CryptoStream decryptedStream = new CryptoStream(base64DecodedStream, decryptor, CryptoStreamMode.Read);
            return decryptedStream;
        }
        private string DecryptString(string cipherText) {
            Aes aes = GetEncryptionAlgorithm();
            byte[] buffer = Convert.FromBase64String(cipherText);
            MemoryStream memoryStream = new MemoryStream(buffer);
            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
            StreamReader streamReader = new StreamReader(cryptoStream);
            return streamReader.ReadToEnd();
        }
        // We have to use same KEY and IV as we use for encryption in angular side.
        // _appSettings.EncryptKey= 1203199320052021
        // _appSettings.EncryptIV = 1203199320052021
        private Aes GetEncryptionAlgorithm() {
            Aes aes = Aes.Create();
            var secret_key = Encoding.UTF8.GetBytes(_appSettings.EncryptKey);
            var initialization_vector = Encoding.UTF8.GetBytes(_appSettings.EncryptIV);
            aes.Key = secret_key;
            aes.IV = initialization_vector;
            return aes;
        }
        // This are excluded URL from encrypt- decrypt that already we added in angular side and as well as in ASP.NET CORE side.
        private List < string > GetExcludeURLList() {
            List < string > excludeURL = new List < string > ();
            excludeURL.Add("/api/Common/commonFileuploaddata");
            excludeURL.Add("/api/Users/UploadProfilePicture");
            excludeURL.Add("/api/Common/downloadattachedfile");
            return excludeURL;
        }
    }
}

Now we need this middleware call from Startup.cs file. See the below screenshot:

Here, all the code is complete. Now you can run the project and see the result. Make sure to use the same 16-digit code for both encryption and decryption. If you want to add a long digit unique code then you can, but make sure its basis is of the AES256 algorithm rule. This means 128 bit(16 digits), 192 bit (24 digits), or 256 bit(32 digits) encryption, Here I have used 128-bit encryption.

If you have any doubts or anything else, please let me know in a comment.