Part - 4 Asymmetric Encryption in ASP - NET Core Web API
Part - 4 Asymmetric Encryption in ASP - NET Core Web API
The primary advantage of Asymmetric Encryption is that it eliminates the need to share a secret key
between the sender and receiver, thus reducing the risk of key compromise.
First create a new ASP.NET Core Web API Project named RSAServerAPP. Then create a folder
named Models within the Project root directory.
namespace RSAServerAPP.Models
{
public class KeyStorage
{
// A private static dictionary to store the RSA keys for each client.
// The dictionary keys are client IDs (string), and the values are tuples containing both
public and private keys.
private static readonly Dictionary<string, (string PublicKey, string PrivateKey)> _keyStore
= new();
// Export the private key in PKCS#8 format and convert it to a base64 string.
var privateKey = Convert.ToBase64String(rsa.ExportPkcs8PrivateKey());
// Store the generated keys in the _keyStore dictionary with the provided clientId as
the key.
_keyStore[clientId] = (publicKey, privateKey);
}
}
namespace RSAServerAPP.Models
{
// The RSAEncryptionService class provides methods to encrypt and decrypt data using
RSA encryption.
public class RSAEncryptionService
{
// Encrypts a string using a public key.
public string Encrypt(string data, string publicKey)
{
// Creates a new RSA instance for cryptographic operations.
using (var rsa = RSA.Create())
{
// Imports the public key, provided in base64 format, to set up the RSA object for
encryption.
rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKey), out _);
// Encrypts the data bytes using the public key and OAEP padding with SHA-256.
var encryptedData = rsa.Encrypt(dataBytes, RSAEncryptionPadding.OaepSHA256);
// Converts the encrypted byte array back to a base64 string for easy storage or
transmission.
return Convert.ToBase64String(encryptedData);
}
}
// Converts the base64 encoded encrypted data back into a byte array.
var dataBytes = Convert.FromBase64String(encryptedData);
// Decrypts the data bytes using the private key and OAEP padding with SHA-256.
var decryptedData = rsa.Decrypt(dataBytes, RSAEncryptionPadding.OaepSHA256);
namespace RSAServerAPP.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EmployeesController : ControllerBase
{
// Static list simulating a database table storing employee records.
private static List<Employee> Employees = new List<Employee>
{
new Employee { Id = 1, Name = "Alice Smith", Position="DBA", Salary = 75000 },
new Employee { Id = 2, Name = "Bob Johnson", Position="HR", Salary = 60000 },
new Employee { Id = 3, Name = "Carol White", Position="Developer", Salary = 55000 }
};
// HTTP GET endpoint to generate RSA keys for a client and return them.
[HttpGet("generate-keys")]
public IActionResult GenerateKeys([FromHeader] string clientId)
{
_keyStorage.GenerateKeys(clientId);
var publicKey = _keyStorage.GetPublicKey(clientId);
var privateKey = _keyStorage.GetPrivateKey(clientId);
return Ok(new { PublicKey = publicKey, PrivateKey = privateKey });
}
// HTTP GET endpoint to retrieve an employee's details, encrypt the data before sending it
back.
[HttpGet("{id}")]
public IActionResult Get(int id, [FromHeader] string clientId)
{
var employee = Employees.FirstOrDefault(e => e.Id == id);
if (employee == null) return NotFound();
// HTTP PUT endpoint to update an existing employee's details from encrypted data.
[HttpPut("{id}")]
public IActionResult Update(int id, [FromHeader] string clientId, [FromBody]
EncryptedRequest request)
{
var privateKey = _keyStorage.GetPrivateKey(clientId);
var decryptedData = _encryptionService.Decrypt(request.Data, privateKey);
var updatedEmployee = JsonSerializer.Deserialize<Employee>(decryptedData);
var employee = Employees.FirstOrDefault(e => e.Id == id);
if (employee == null) return NotFound();
employee.Name = updatedEmployee.Name;
employee.Position = updatedEmployee.Position;
employee.Salary = updatedEmployee.Salary;
var encryptedResponse =
_encryptionService.Encrypt(JsonSerializer.Serialize(employee),
_keyStorage.GetPublicKey(clientId));
return Ok(new EncryptedDataResponse { Data = encryptedResponse });
}
Employees.Remove(employee);
return Ok();
}
}
RSAClientAPP
Next, we need to create the Client APP consuming the service with RSA encryption and decryption.
For this, we are going to create a Console Application. So, create a Dot Net Console Application
named RSAClientAPP. Once you create the Console Application, modify the Program class code as
follows:
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace RSAClientAPP
{
public class Program
{
// HttpClient instance for sending requests to a server.
private static readonly HttpClient client = new HttpClient();
// Request to generate RSA keys from the server and retrieve them.
var keysResponse = await
client.GetFromJsonAsync<KeysResponse>("https://localhost:7102/api/Employees/generate-
keys");
var publicKey = keysResponse.PublicKey;
var privateKey = keysResponse.PrivateKey;
// Encrypt the new employee data using the public key and send to the server.
var encryptedData = EncryptData(newEmployee, publicKey);
var createRequest = new EncryptedRequest { Data = encryptedData };
// Send the encrypted employee data to the server to create a new employee.
var createResponse = await
client.PostAsJsonAsync("https://localhost:7102/api/Employees/create", createRequest);
var createdEmployeeData = await
createResponse.Content.ReadFromJsonAsync<EncryptedDataResponse>();
var createdEmployeeJson = DecryptData(createdEmployeeData.Data, privateKey);
var createdEmployee = JsonSerializer.Deserialize<Employee>(createdEmployeeJson);
Console.WriteLine($"Created Employee: {JsonSerializer.Serialize(createdEmployee)}");
// Retrieve the created employee's data from the server and decrypt it.
var getResponse = await
client.GetAsync($"https://localhost:7102/api/Employees/{createdEmployee.Id}");
var encryptedEmployee = await
getResponse.Content.ReadFromJsonAsync<EncryptedDataResponse>();
var decryptedEmployeeJson = DecryptData(encryptedEmployee.Data, privateKey);
var decryptedEmployee =
JsonSerializer.Deserialize<Employee>(decryptedEmployeeJson);
Console.WriteLine($"Retrieved Employee:
{JsonSerializer.Serialize(decryptedEmployee)}");
// Update the employee's name, encrypt the updated data, and send it to the server.
createdEmployee.Name = "Jane Doe";
encryptedData = EncryptData(createdEmployee, publicKey);
var updateRequest = new EncryptedRequest { Data = encryptedData };
var updateResponse = await
client.PutAsJsonAsync($"https://localhost:7102/api/Employees/{createdEmployee.Id}",
updateRequest);
var updatedEmployeeData = await
updateResponse.Content.ReadFromJsonAsync<EncryptedDataResponse>();
var updatedEmployeeJson = DecryptData(updatedEmployeeData.Data, privateKey);
var updatedEmployee = JsonSerializer.Deserialize<Employee>(updatedEmployeeJson);
Console.WriteLine($"Updated Employee:
{JsonSerializer.Serialize(updatedEmployee)}");
Key Usage:
Symmetric Encryption: Both the sender and receiver use the same secret key for encryption
and decryption. This requires that the key must be securely exchanged and kept confidential.
Asymmetric Encryption: Involves two keys: a public key for encryption and a private key for
decryption. The public key can be freely distributed, while the private key is kept secure by
the owner.
Key Management:
Symmetric Encryption: Requires secure key distribution mechanisms, as the same key
must be shared between communicating parties. This can be challenging in large-scale
environments.
Asymmetric Encryption: Simplifies key distribution since the public key can be shared
openly without compromising security. Only the private key must be protected.
Speed:
Symmetric Encryption: Generally, much faster than asymmetric encryption due to the
simplicity of the algorithms used. It is well-suited for encrypting large amounts of data.
Asymmetric Encryption: Slower because of the complexity of the algorithms. Typically used
for encrypting small amounts of data, such as keys or digital signatures.
Security:
Symmetric Encryption: Security depends on the strength of the encryption algorithm and
the secrecy of the key. If the key is compromised, the security is breached.
Asymmetric Encryption: Offers higher security due to the use of two keys. Even if the public
key is known, without the private key, it is computationally infeasible to decrypt the data.
Examples of Algorithms:
Symmetric Encryption: Common algorithms include AES (Advanced Encryption Standard),
DES (Data Encryption Standard), and 3DES (Triple DES).
Asymmetric Encryption: Common algorithms include RSA (Rivest-Shamir-Adleman), ECC
(Elliptic Curve Cryptography), and DSA (Digital Signature Algorithm).