Introduction
Nowadays, clients want their websites to be safe; they want no one to access the entire payload or the id parameter value that we supply during the API request so that you can receive the correct solution.
I have discussed everything that is more useful for security in this article.
In this article, I will describe how to use Angular 11 (front end) with ASP.NET CORE to encrypt and decrypt the entire payload or any given id (API).
In both Angular and ASP.NET CORE, I used the AES256 algorithm. Utilizing middleware that I will walk you through step by step, you can use the same approach to get the payload or ids in ASP.NET CORE while using an interceptor to pass the payload or id with encryption on the angular side.
Make sure to use the same algorithm for decryption as we did for encryption if you use any front-end framework or angular-based approach.
Let me now go over everything step by step. Let’s start with the Angular code.
You must first create a project. If not, construct everything and carry out any CRUD operations as well as any GET and POST-based functionality; alternatively, you can add it to your current project.
Angular Code
Step 1
Using the npm terminal, install the crypto js. Use the command below to install crypto js.
npm i crypto-js
You can use it based on your angular version; I used version 4.1.1.
The next step is to establish a service to encrypt the payload or parameter after installing Crypto Js.
Step 2
Create a service that uses the AES256 algorithm to encrypt and decrypt the payload.
We must obtain a special 16-digit code for the encryption keys and IV before we can encrypt and decrypt using the same keys and IV on the asp.net code side.
To decode the code, I am using the same ID here. You can use a different ID, but you must use the same key and IV.
environment.EncryptKey = 1203199320052021
environment.EncryptIV = 1203199320052021
Here I have created a service and given 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); } }
Therefore, we will use the functions from the interceptor method since the aforementioned code is for encrypting and decrypting the payload and parameter.
A unique class of angular services that we can use is HTTP Interceptors. The core point between the client-side and server-side outgoing/incoming HTTP requests and responses is where custom logic is applied. Remember that just HTTP requests are what the interceptor wants.
Let’s create interceptor.
Step 3
Before providing the encrypted payload to the API service request, create the interceptor file.
Encrypt-decrypt-interceptor.ts was the name I gave my interceptor creation.
See the interceptor method’s code below.
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 draw attention to the fact that you can simply add the API URL to the ExcludeURLList as displayed in Interceptor if you wish to exclude an API call from encryption. Many of the functions or API calls in our project don’t require encryption, so we may handle them in other ways. Assume that I add some API calls that are connected to uploading images to the exclude list since I don’t need to encrypt the images before uploading them.
We now need to set up the module file so that every time we contact the API, it calls the interceptor first, encrypts the payload, and then passes the encrypted payload to the API (GET or POST).
Step 4: Configure interceptor in app.module.ts file
Step 4: Configure interceptor in app.module.ts file
Prior to configuring Interceptor into the provider, import the inceptor that we built (encrypt-decrypt-interceptor.ts).
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; }
As seen in the code above, add Interceptor to providers.
The Angular front-end side code is now finished. Using the methods listed above, encrypt the payload and parameter to prevent anyone from seeing their original forms.
See the illustration above to grasp encryption. No one can see the original payload or parameter value because it is completely encrypted.
ASP.NET Core Code
The payload must be decrypted using the same algorithm in the Asp.Net core before being passed to the Action method.
In order to decrypt the payload before performing the Controller Action method, I have constructed one middleware helper. The main advantage is that we did not have to write a lot of code for anything because we could just use middleware and call it from the Startup.cs file to call middleware on each ASP.NET CORE request.
In ASP.Net Core, we can set an exclude list similarly to Angular.
Software components known as middleware are put together into an application pipeline to handle requests and responses. Each component in the pipeline decides whether to move the request on to the next component in the pipeline and has some control over what happens both before and after the next component is called.
The payload and parameter that are being passed from Angular must first be encrypted and decrypted using a middleware helper.
Copy and paste the code below into a middleware helper with the name EncryptionMiddleware.cs.
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 need this middleware call from Startup.cs file, See below screenshot
As a result, the code is finished here. You can now execute the project and view the outcome. The 16-digit code must be the same for both encryption and decryption, though. If you wish to add a long, unique code, you can do so as long as it adheres to the AES256 algorithm regulation, which calls for 128-bit encryption (16 digits), 192-bit encryption (24 digits), or 256-bit encryption (32 digits). In this case, I’ve chosen 128-bit encryption.
Please leave a remark if you have any questions or concerns.