Azure Key Vaults And .Net Management
Today, we will discuss Azure Key Vaults in detail, exploring what they are, how they help securely store sensitive information like secrets and keys, and then dive into how we can integrate this functionality into a .NET project step by step.
Azure Key Vault is a centralized service designed to securely store sensitive application data. This includes important information like passwords, API keys, certificates, or encryption keys, all stored in a single, secure location.
It ensures that only authorized people or applications can access the data, enhancing security while making it easier to manage. In simple terms, it acts as a secure vault for your application’s critical information.
Vaults:
A key vault is like a secure safe designed to protect sensitive information, such as passwords, cryptographic keys, or other confidential data. When moving to the cloud, it’s important to keep different types of sensitive data separate for better security. Using multiple key vaults helps you organize and protect this data by keeping it logically isolated from each other.
Each key vault acts as a container for securely storing **keys** (used for encryption and decryption) and **secrets** (like passwords or API keys). The key vault also manages who can access these keys and secrets, ensuring only authorized users or applications can use them.
Secrets:
It is used to securely store and hold authorization and authentication information, API keys, passwords, connection strings, license keys and other sensitive information.
Keys:
A cryptographic key is created for a specific purpose. For example, you can store a Redis URL or a SQL database connection string securely in a key vault.
Once these keys are stored in the key vault, applications don’t get direct access. Instead, the app sends a request to the key vault to perform actions like encrypting or decrypting data, and the key vault handles these operations securely within its protected environment.
The keys in the vault can either be standalone or versioned, meaning you can update a key while keeping older versions archived for reference or rollback if needed.
Certificates:
Azure Key Vault Certificates is a service for securely storing, managing, and automating the lifecycle of X.509 certificates used for authentication and encryption. It enables secure storage, automatic renewal with trusted Certificate Authorities, and easy integration with Azure services like App Service and AKS. This helps centralize certificate management, enhance security, and simplify deployment.
There is no such thing as perfect security, only varying levels of insecurity. — Salman Rushdie
Create Key Vaults Service
You can create key vaults service as seen below.
You have to fill in all the inputs.
For Authentication, I added my Azure Microsoft Account for Access policies.
I gave all permissions to my self for “boraKeyVault” service. You can see Access configuration as seen below.
On Visual Studio 2022 Tools => Options => Azure Service Authentication, I used my Azure Microsoft account to access the Azure Key Vault service without requiring any credentials.
Creating Azure Key Vaults Manager Class:
When accessing the Azure Key Vault service, organizing all possible operations under a single interface makes your application more maintainable and extensible. Below is an example of how these operations can be structured using a C# interface:
This interface includes common operations like managing secrets, certificates, and optionally keys, depending on your requirements.
IAzureKeyVaultManager.cs:
The following are all the operations that can be performed with the Azure Key Vault service.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureKeyVault
{
public interface IAzureKeyVaultManager
{
string ReadSecretFromAzureKeyVault(string key);
Dictionary<string, string> ReadSecretsFromAzureKeyVault(string[] keys);
bool AddSecretToAzureKeyVault(string name, string value);
bool AddSecretsToAzureKeyVault(Dictionary<string, string> secrets);
bool AddSecretToAzureKeyVaultEvenIfDeleted(string name, string value);
bool AddSecretsToAzureKeyVaultEvenIfDeleted(Dictionary<string, string> secrets);
bool DeleteSecretFromAzureKeyVault(string key);
bool DeleteSecretsFromAzureKeyVault(string[] secrets);
string GenerateVaultKey(VaultLocation location, string entity, string company, string key);
}
}
Add First Secret To Azure Key Vault:
The client is our Azure “SecretClient” and we will use the “SetSecret()” method with name and value to set the secret to the KeyVaults service.
The connection to the Azure service was established without using any secrets or keys. Instead, it leveraged “Azure Service Authentication” through Visual Studio, which simplifies development and avoids exposing sensitive credentials. For production environments, connections will be made using predefined certificates on live machines.
This approach enhances security by ensuring that authentication relies on managed identities or secure certificates rather than static secrets. It also helps prevent unauthorized access to Azure resources by eliminating the risk of credential leakage. Additionally, using certificates aligns with best practices for securing Azure services and supports robust access control policies.
public bool AddSecretToAzureKeyVault(string name, string value)
{
// Name of the secret to be stored in Key Vault
string secretName = name;
//The value of the secret to be stored in Key Vault
string secretValue = value;
try
{
//Creating Azure Key Vault client
var client = GetSecretClient();
// Add Secret to Key Vault
Console.WriteLine($"'{secretName}' The secret named is added to the Key Vault...");
client.SetSecret(secretName, secretValue);
Console.WriteLine("Secret added successfully!");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
return false;
}
}
Isolation doesn’t bother me at all. It gives me a sense of security. — Jimmy Page
Create Secret Client:
We will use Azure Service Url and for security, we will use DefaultAzureCredential. We will use same SecretClient class, for all operations.
private SecretClient GetSecretClient()
{
string keyVaultUrl = "https://********.vault.azure.net/";
return new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
}
You can find Vault URL from Azure Portal.
Add List of Secrets To Azure Key Vault:
There is no Bulk operation in the “Azure.Security.KeyVault.Secrets.SecretClient” class, so we have to insert each secret one by one. Mybe we can use “Parallel.ForEach” for improving the performance. However, I didn’t want to take the risk of the thread pool becoming overloaded and the application halting due to having too many keys.
“Azure Key Vault, who can use what type It uses Access Policies and Role-Based Access Control (RBAC) to determine who has access. This ensures that only authorized users and applications can access encrypted data.
“If I were you, I would enhance security by encrypting all secrets using a library such as ‘CryptographyClient’ before manually storing them, ensuring that they cannot be viewed in plain text by unauthorized individuals, even if accessed.”
public bool AddSecretsToAzureKeyVault(Dictionary<string, string> secrets)
{
try
{
var client = GetSecretClient();
// Add secrets in bulk..
Console.WriteLine("Secrets are added to Key Vault...");
foreach (var secret in secrets)
{
client.SetSecret(secret.Key, secret.Value);
Console.WriteLine($"'{secret.Key}' The secret named has been added successfully!");
}
Console.WriteLine("All secrets added successfully!");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
return false;
}
}
Add Same Secret To Azure Key Vault Even If Deleted:
When you try to save an existing secret to the Azure KeyVault service, it won’t cause any issues and will overwrite the old one with the new value.
However, the real issue arises when trying to save a deleted secret with the same key. In this case, the system checks whether the key being saved has been deleted. If the answer is yes, the key is recovered and restored to an undeleted state. After this process, the new secret is saved by overwriting the existing one.
Tip: You have to wait for the recovery of deleted secret with the “recover option.WaitForCompletion()” method.. If you don’t, you will get an error when you try to update!
Secrets are never truly deleted unless they are permanently purged for security reasons. Instead, they are soft-deleted and moved to a previous versions list. This allows you to access history list of a secret during emergencies if needed.
public bool AddSecretToAzureKeyVaultEvenIfDeleted(string name, string value)
{
string secretName = name;
string secretValue = value;
try
{
//Creating Azure Key Vault client
var client = GetSecretClient();
// Check if the secret exists
try
{
var existingSecret = client.GetSecret(secretName);
Console.WriteLine($"'{secretName}' The secret named already exists. Updating...");
client.SetSecret(secretName, secretValue);
Console.WriteLine("Secret updated successfully!");
return true;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
// Secret mevcut değil, devam edin
Console.WriteLine($"'{secretName}' The secret named was not found. New secret is added...");
}
// Check if it is deleted but recoverable
try
{
var deletedSecret = client.GetDeletedSecret(secretName);
Console.WriteLine($"'{secretName}' The secret named has been deleted but is recoverable. Being rescued...");
var recoverOption = client.StartRecoverDeletedSecret(secretName);
recoverOption.WaitForCompletion();
Console.WriteLine("Secret was rescued. Updating...");
client.SetSecret(secretName, secretValue);
Console.WriteLine("Secret updated successfully!");
return true;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
// Not recoverable, continue
Console.WriteLine($"'{secretName}' The secret named is not recoverable. New secret is added...");
}
//Add new secret
client.SetSecret(secretName, secretValue);
Console.WriteLine("Secret added successfully!");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
return false;
}
}
AddSecretsToAzureKeyVaultEvenIfDeleted(): This method operates in the same way as the single-secret addition method. The difference is that instead of adding just one secret, this method repeats the same process for each secret in the list, applying it multiple times to handle all the secrets.
Read Secrets:
The secret key to be searched is requested from the SecretClient. If the specified key does not exist, an error is thrown; if it does exist, its value is returned.
public string ReadSecretFromAzureKeyVault(string key)
{
// Name of the secret stored in Key Vault
string secretName = key;
try
{
// Creating Azure Key Vault client
var client = GetSecretClient();
// Getting the secret value
Console.WriteLine($"'{secretName}' The secret named is retrieved from Key Vault....");
KeyVaultSecret secret = client.GetSecret(secretName);
// Secret değerini elde etme
string secretValue = secret.Value;
Console.WriteLine("Obtaining the secret value!");
Console.WriteLine($"Secret Value: {secretValue}");
return secretValue;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
// Secret mevcut değil, devam edin
Console.WriteLine($"'{secretName}' The secret named was not found...");
return string.Empty;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
return string.Empty;
}
}
Our only security is our ability to change. — John Lilly
Bulk Read Secrets:
The keys to be searched are taken as an array parameter. Each key is searched individually. For the secrets that are not found, an empty string is assigned as the value. A dictionary list is returned in the form of key-value pairs.
public Dictionary<string, string> ReadSecretsFromAzureKeyVault(string[] keys)
{
var secrets = new Dictionary<string, string>();
try
{
// Azure Key Vault istemcisi oluşturuluyor
var client = GetSecretClient();
Console.WriteLine("Secrets are taken from the Key Vault in bulk...");
foreach (var key in keys)
{
try
{
Console.WriteLine($"Retrieving the secret named '{key}'...");
KeyVaultSecret secret = client.GetSecret(key);
secrets[key] = secret.Value;
Console.WriteLine($"The secret named '{key}' was successfully retrieved!");
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine($"The secret named '{key}' was not found...");
secrets[key] = string.Empty;
}
catch (Exception ex)
{
Console.WriteLine($"'{key}' An error occurred while retrieving the secret named: {ex.Message}");
secrets[key] = string.Empty;
}
}
Console.WriteLine("All secrets reading process is completed.");
}
catch (Exception ex)
{
Console.WriteLine($"General Error occurred: {ex.Message}");
}
return secrets;
}
Delete Secret:
Our goal is to delete a secret from the Azure Key Vault, but only if it exists. We will use the “client.StartDeleteSecret()” method to attempt the deletion. If a “RequestFailedException” is encountered, it means that no secret with the specified key is present in the Key Vault.
public bool DeleteSecretFromAzureKeyVault(string secretName)
{
try
{
// Creating Azure Key Vault client
var client = GetSecretClient();
Console.WriteLine($"'{secretName}' the secret named is being deleted from Key Vault...");
// Start secret deletion process
client.StartDeleteSecret(secretName);
Console.WriteLine($"'{secretName}' the secret named has been deleted successfully!");
return true;
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine($"'{secretName}' the secret named was not found.");
return false;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
return false;
}
}
Only the one who has lost the need for security is truly secure. — Sadhguru
Delete Secrets With Bulk:
We will perform a soft deletion of a list of secrets from the Azure Key Vault. However, the “SecretClient” class does not provide a bulk delete operation. As a result, we will delete each secret individually. If a secret cannot be found, an exception will be thrown, but the process will continue to delete the remaining secrets.
public bool DeleteSecretsFromAzureKeyVault(string[] secrets)
{
try
{
//string keyVaultUrl = "https://borakeyvault.vault.azure.net/";
bool allSuccess = true; // To track the success status of all transactions
var client = GetSecretClient();
Console.WriteLine("Secrets are deleted from Key Vault...");
foreach (var secret in secrets)
{
try
{
client.StartDeleteSecret(secret);
Console.WriteLine($"The secret named '{secret}' has been deleted successfully!");
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine($"The secret named '{secret}' was not found. Deletion skipped.");
allSuccess = false; // En az bir işlem başarısız oldu
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while deleting the secret named '{secret}': {ex.Message}");
allSuccess = false; // At least one operation failed
}
}
if (allSuccess)
{
Console.WriteLine("All secrets deleted successfully!!");
}
else
{
Console.WriteLine("Errors occurred while deleting some secrets.");
}
return allSuccess;
}
catch (Exception ex)
{
Console.WriteLine($"Error occurred: {ex.Message}");
return false;
}
}
Bonus: Creating Name For Secrets
To standardize the naming of your secrets, you can create a method that follows a specific naming pattern. In the example below, we use a combination of location, entity, company, and key patterns to ensure the secrets are easy to understand, unique, and simple to locate.
public string GenerateVaultKey(VaultLocation location, string entity, string company, string key)
{
return string.Format("{0}:{1}:{2}:{3}", location, entity, company, key);
}
Test:
program.cs:
In the following example, three operations were performed: first, a bulk insert, then a bulk read, and finally, a bulk delete.
//Bulk Insert
var secrets = new Dictionary<string, string>
{
{ "ApiKey", "12345624" },
{ "DbPassword", "MySecurePassword4" },
{ "StorageConnectionString", "DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey;4" }
};
akvManager.AddSecretsToAzureKeyVaultEvenIfDeleted(secrets);
// Get List
var secrets2 = new string[] { "ApiKey", "DbPassword", "StorageConnectionString" };
var result = akvManager.ReadSecretsFromAzureKeyVault(secrets2);
foreach (var item in result)
{
Console.WriteLine(item.Key + " : " + item.Value);
}
//Bulk Delete
akvManager.DeleteSecretsFromAzureKeyVault(secrets2);
Conclusion:
In conclusion, Azure Key Vault is an essential tool for modern applications, providing a robust and secure way to manage sensitive information such as secrets, cryptographic keys, and certificates. Its integration with .NET simplifies secure access and management, enabling developers to focus on application logic without compromising security.
By leveraging Azure Key Vault, developers can ensure that sensitive credentials and secrets are never exposed in their application code or configurations. Features like seamless authentication through `DefaultAzureCredential` and managed identities further enhance security by eliminating the need for static credentials. Moreover, the ability to version keys, automate certificate lifecycles, and perform secure operations in an isolated environment makes it a versatile solution for enterprises.
While the lack of bulk operations requires careful implementation for performance optimization, techniques like asynchronous methods and parallel processing provide effective workarounds. Structured approaches, such as using interfaces for key vault operations, enhance maintainability and scalability, making it easier to adapt to changing requirements.
By incorporating Azure Key Vault into your .NET projects, you are adopting a best-practice approach to cloud security, ensuring your applications remain resilient, compliant, and secure in the face of evolving threats.
See you until the next article.
“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”
Source [Github]: https://github.com/borakasmer/AzureKeyVaults
Source:
1. https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates
2. https://azure.microsoft.com/en-us/products/key-vault
3. https://endjin.com/blog/2020/05/how-does-azure-key-vault-help-me-secure-my-data