Azure Storage Client Side Encryption - c#

I'm trying to test client side encryption with an azure storage account. So far I've created a resource group and put my KeyVault, Registered App on Active Directory and inside my keyVault I've created a secret.
I think im failing to map my secret to my storage account, but I figured that they should work if they are in the same resource group.
$key = "qwertyuiopasdfgh"
$b = [System.Text.Encoding]::UTF8.GetBytes($key)
$enc = [System.Convert]::ToBase64String($b)
$secretvalue = ConvertTo-SecureString $enc -AsPlainText -Force
$secret = Set-AzureKeyVaultSecret -VaultName 'ectotecStorageKeyVault' -Name 'ectotecSecret' -SecretValue $secretvalue -ContentType "application/octet-stream"
The problem is that im getting an invalid secret provided error with the following code:
namespace cifradoApp
{
class Program
{
private async static Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(
ConfigurationManager.AppSettings["clientId"],
ConfigurationManager.AppSettings["clientSecret"]);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
static void Main(string[] args)
{
// This is standard code to interact with Blob storage.
StorageCredentials creds = new StorageCredentials(
ConfigurationManager.AppSettings["accountName"],
ConfigurationManager.AppSettings["accountKey"]
);
CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);
CloudBlobClient client = account.CreateCloudBlobClient();
CloudBlobContainer contain = client.GetContainerReference(ConfigurationManager.AppSettings["container"]);
contain.CreateIfNotExists();
// The Resolver object is used to interact with Key Vault for Azure Storage.
// This is where the GetToken method from above is used.
KeyVaultKeyResolver cloudResolver = new KeyVaultKeyResolver(GetToken);
// Retrieve the key that you created previously.
// The IKey that is returned here is an RsaKey.
// Remember that we used the names contosokeyvault and testrsakey1.
var rsa = cloudResolver.ResolveKeyAsync("https://ectotecstoragekeyvault.vault.azure.net/secrets/ectotecSecret/dee97a40c78a4638bbb3fa0d3e13f75e", CancellationToken.None).GetAwaiter().GetResult();
// Now you simply use the RSA key to encrypt by setting it in the BlobEncryptionPolicy.
BlobEncryptionPolicy policy = new BlobEncryptionPolicy(rsa, null);
BlobRequestOptions options = new BlobRequestOptions() { EncryptionPolicy = policy };
// Reference a block blob.
CloudBlockBlob blob = contain.GetBlockBlobReference("BlobPruebaEncrypted.txt");
// Upload using the UploadFromStream method.
using (var stream = System.IO.File.OpenRead(#"C:\Users\moise\Desktop\ectotec stuff\Visual Studio\azureStorageSample\container\BlobPrueba.txt"))
blob.UploadFromStream(stream, stream.Length, null, options, null);
}
}
}
My app settings seems to be working fine, since i valide before with only my account and key to access the storage account, since I made tests without trying to do client side encryption, everything worked out just fine. The problem comes with the secret it seems.
ERROR WHEN I TRY TO UPLOAD SOMETHING TO MY STORAGE ACCOUNT CONTAINER(BLOB)
AdalException: {"error":"invalid_client","error_description":"AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.\r\nTrace ID: 52047a12-b950-4d8a-9206-120e383feb00\r\nCorrelation ID: e2ad8afe-4272-49aa-94c0-5dad435ffc45\r\nTimestamp: 2019-01-02 17:10:32Z","error_codes":[70002,50012],"timestamp":"2019-01-02 17:10:32Z","trace_id":"52047a12-b950-4d8a-9206-120e383feb00","correlation_id":"e2ad8afe-4272-49aa-94c0-5dad435ffc45"}: Unknown error
<appSettings>
<add key="accountName" value="sampleExample"/>
<add key="accountKey" value="KeyForMyApp"/>
<add key="clientId" value="app-id"/>
<add key="clientSecret" value="qwertyuiopasdfgh"/>
<add key="container" value="ectotec-sample2"/>
</appSettings>
I'm trying to replicate the example in this tutorial:
https://learn.microsoft.com/en-us/azure/storage/blobs/storage-encrypt-decrypt-blobs-key-vault

You need to make sure that you have granted your appliation rights to read keys. This is seperate from the RBAC permissions on the Key Vault.
To do this, browse to teh Key Vault in the portal, on the menu on the left you will see a settings section, and under here an item called "access policies", click on this.
You then want to click the "Add New" button. In the window that opens, click on the "Select Principal" section, and then enter in the name or application ID of the application you want to have access. Select the appropriate permissions for keys, secrets or certificates and then click OK.
This will take you back to the list of authorised users, be sure to click save at the top left (it isn't obvious you need to do this), your app should then have access.

Related

Azure Key Vault and Certificate - .NET Framework ClientCertificateCredential access to Secrets

I have generated .pfx, .pvk and .cer certification files.
In Azure:
I created a new Vault, let's call it MyVault
In MyVault, I created a Secret called SubscriptionKey
MyVault has a Certificates section to which I've uploaded MyCertificate.cer file.
Confusingly enough, Azure also has a "Azure Active Directory" section where I can also upload Certificates. This is what I understood from researching, to be the place where to upload the certificate, and get the associated clientId and tenantId needed for the ClientCertificateCredential constructor.
Goal: Retrieve the secret value from MyVault using a Certificate and the code:
public static string GetSecretFromAzureKeyVault(string secretName)
{
string vaultUrl = "https://MyVault.vault.azure.net/";
string cerPath = "C:\\Personal\\MyCertificate.cer";
ClientCertificateCredential credential = new(
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
cerPath
);
SecretClient client = new(new Uri(vaultUrl), credential);
KeyVaultSecret secret = client.GetSecret(secretName);
return secret.Value;
}
When running the code I'm still getting null for the line:
KeyVaultSecret secret = client.GetSecret(secretName);
Any suggestions on what I've done wrong in this flow or regarding the resources?
EDIT:
Error screenshot:
I have followed the below steps and got the secret value
Create an app from AAD and register the app using APP registrations.
Create a keyVault and secret. And use the secret name in the code.
Use the ClientId and TenantId from the App registrations and use it in the code.
Download the .pfx format file and use the certificate in the code.
Use .pfx downloaded path in code
public static string GetSecretFromAzureKeyVault(string secretName)
{
string vaultUrl = "https://keyvault.vault.azure.net/";
string cerPath = "C:\\Tools\\keyvault-keycertificate-20230109.pfx";
ClientCertificateCredential credential =
new ClientCertificateCredential("TenantId", "ClientId", cerPath);
SecretClient client = new SecretClient(new Uri(vaultUrl), credential);
KeyVaultSecret secret = client.GetSecret(secretName);
return secret.Value;
}
You can find the secret value in the below highlighted screen.

Azure Key Vault Add Access Policy with C#

I am trying to retrieve all the Certificates, Keys and Secrets from a Key Vault in order to perform a compliance test of it´s settings. I was able to create a Key Vault Client using Azure Management SDK,
KeyVault Client objKeyVaultClient = new KeyVaultClient(
async (string authority, string resource, string scope) =>
{
...
}
);
and trying to retrieve the certificates / keys / secrets with:
Task<IPage<CertificateItem>> test = objKeyVaultClient.GetCertificatesAsync(<vaultUri>);
However, first I need to set the access policies with List and Get permissions. In PowerShell I achieve this with:
Set-AzKeyVaultAccessPolicy -VaultName <VaultName> -UserPrincipalName <upn> -PermissionsToKeys List,Get
Do you know a way that I can do the same in C#?
If you want to manage Azure key vault access policy with Net, please refer to the following steps
create a service principal (I use Azure CLI to do that)
az login
az account set --subscription "<your subscription id>"
# the sp will have Azure Contributor role
az ad sp create-for-rbac -n "readMetric"
Code
// please install sdk Microsoft.Azure.Management.Fluent
private static String tenantId=""; // sp tenant
private static String clientId = ""; // sp appid
private static String clientKey = "";// sp password
private static String subscriptionId=""; //sp subscription id
var creds= SdkContext.AzureCredentialsFactory.FromServicePrincipal(clientId,clientKey,tenantId,AzureEnvironment.AzureGlobalCloud);
var azure = Microsoft.Azure.Management.Fluent.Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(creds)
.WithSubscription(subscriptionId);
var vault = await azure.Vaults.GetByResourceGroupAsync("group name", "vault name");
await vault.Update().DefineAccessPolicy()
.ForUser("userPrincipalName")
.AllowKeyPermissions(KeyPermissions.Get)
.AllowKeyPermissions(KeyPermissions.List)
.Attach()
.ApplyAsync();

Roles required to use PersistKeysToAzureBlobStorage with AzureServiceTokenProvider

I want my application to be able to initialize the data protection key storage at startup.
But it will only work if I use the Azure Storage Access Key and not the MSI or Azure user in my Visual Studio (az login) that I expected. I have given the user and VM Scale Set the roles Owner, Contributor, Storage blob Data Contributor without success.
Is it a must to use SAS or the Access Key in order for the blob to be created automatically?
Error when using the token provider (MSI, az login)
Microsoft.AspNetCore.DataProtection.Internal.DataProtectionHostedService:
Information: Key ring failed to load during application startup.
Request Information
StatusMessage:The specified resource does not exist.
ErrorCode:ResourceNotFound
ErrorMessage:The specified resource does not exist.
public void ConfigureServices( IServiceCollection services )
{
var accessKey = "ddBU/...==";
var blobUri = new Uri( $"https://mystorageaccount.blob.core.windows.net/mycontainer/keys.xml" );
var tp = new Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider();
var token = tp.GetAccessTokenAsync( $"https://mystorageaccount.blob.core.windows.net/" ).Result;
// OK - creates and updates blob when neccessary
var sc = new Microsoft.Azure.Storage.Auth.StorageCredentials( "mystorageaccount", accessKey );
// NOK - can only read the blob
//sc = new Microsoft.Azure.Storage.Auth.StorageCredentials( token );
var cbb = new Microsoft.Azure.Storage.Blob.CloudBlockBlob( blobUri, sc );
services.AddDataProtection()
.PersistKeysToAzureBlobStorage( cbb )
.SetApplicationName( "MyFrontends" );
}
The role "Storage blob Data Contributor" does allow PersistKeysToAzureBlobStorage() to create and maintain the key in the Azure Storage Account.
I must have made a mistake while testing, maybe I did not wait long enough after applying the role or maybe I had not created the target container.

How to export azure database to blob storage

I need to know exactly how to login to Azure, using c#.
I basically want to do this, but from the code:
]a link](https://learn.microsoft.com/en-us/azure/sql-database/sql-database-export)
Here is the code I copied from the internet trying to achieve this:
But I don't know how to generate the token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var exportParams = new DacExportParameters()
{
BlobCredentials = new DacExportParameters.BlobCredentialsParameter()
{
StorageAccessKey = storageKey,
Uri = new Uri(baseStorageUri)
},
ConnectionInfo = new DacExportParameters.ConnectionInfoParameter()
{
ServerName = azureSqlServer,
DatabaseName = azureSqlDatabase,
UserName = adminLogin,
Password = adminPassword
}
};
var exportResult = managementClient.Dac.Export(azureSqlServerName, exportParams);
I have a GetToken function, but I have no idea where to take the
tenant + client id + secret
private static string GetAccessToken(string tenantId, string
clientId, string secretKey)
{
var authenticationContext = new
AuthenticationContext($"https://login.windows.net/{tenantId}");
var credential = new ClientCredential(clientId, secretKey);
var result =authenticationContext
.AcquireTokenAsync("https://management.core.windows.net/",
credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
var token = result.Result.AccessToken;
return token;
}
This question was asked before
Azure Database export with C#
but I need to see the actual code and explanation on how to get the connection info.
I need to see the actual code and explanation on how to get the connection info.
I would recommend you follow this tutorial about registering your AAD application and adding the secret key. Moreover, you could also follow Using the Azure ARM REST API – Get Access Token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
Based on your code, I assumed that you are using the package Microsoft.WindowsAzure.Management.Sql, if you use the TokenCloudCredentials, you may receive the following error response:
AFAIK, Microsoft.WindowsAzure.Management.Libraries requires the X509Certificate2 authentication, you need to construct the CertificateCloudCredentials for your SqlManagementClient. For uploading a management certificate under your subscription, you could follow Upload an Azure Service Management Certificate. For retrieving the X509Certificate2 instance, you could follow the code snippet under the Authenticate using a management certificate section from here.
For token-based authentication, you could use the package Microsoft.Azure.Management.Sql and construct your SqlManagementClient as follows:
var sqlManagement = new SqlManagementClient(new TokenCredentials("{access-token}"));
Moreover, you need to change the resource from https://management.core.windows.net/ to https://management.azure.com/ when invoking the AcquireTokenAsync method.

Error when calling any method on Service Management API

I'm looking to start an Azure runbook from a c# application which will be hosted on an Azure web app.
I'm using certificate authentication (in an attempt just to test that I can connect and retrieve some data)
Here's my code so far:
var cert = ConfigurationManager.AppSettings["mgmtCertificate"];
var creds = new Microsoft.Azure.CertificateCloudCredentials("<my-sub-id>",
new X509Certificate2(Convert.FromBase64String(cert)));
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(creds, new Uri("https://management.core.windows.net/"));
var content = client.Runbooks.List("<resource-group-id>", "<automation-account-name>");
Every time I run this, no matter what certificate I use I get the same error:
An unhandled exception of type 'Hyak.Common.CloudException' occurred in Microsoft.Threading.Tasks.dll
Additional information: ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I've tried downloading the settings file which contains the automatically generated management certificate you get when you spin up the Azure account... nothing I do will let me talk to any of the Azure subscription
Am I missing something fundamental here?
Edit: some additional info...
So I decided to create an application and use the JWT authentication method.
I've added an application, given the application permissions to the Azure Service Management API and ensured the user is a co-administrator and I still get the same error, even with the token...
const string tenantId = "xx";
const string clientId = "xx";
var context = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantId));
var user = "<user>";
var pwd = "<pass>";
var userCred = new UserCredential(user, pwd);
var result = context.AcquireToken("https://management.core.windows.net/", clientId, userCred);
var token = result.CreateAuthorizationHeader().Substring("Bearer ".Length); // Token comes back fine and I can inspect and see that it's valid for 1 hour - all looks ok...
var sub = "<subscription-id>";
var creds = new TokenCloudCredentials(sub, token);
var client = new AutomationManagementClient(creds, new Uri("https://management.core.windows.net/"));
var content = client.Runbooks.List("<resource-group>", "<automation-id>");
I've also tried using other Azure libs (like auth, datacentre etc) and I get the same error:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I'm sure it's just 1 tickbox I need to tick buried somewhere in that monolithic Management Portal but I've followed a few tutorials on how to do this and they all end up with this error...
public async Task StartAzureRunbook()
{
try
{
var subscriptionId = "azure subscription Id";
string base64cer = "****long string here****"; //taken from http://stackoverflow.com/questions/24999518/azure-api-the-server-failed-to-authenticate-the-request
var cert = new X509Certificate2(Convert.FromBase64String(base64cer));
var client = new Microsoft.Azure.Management.Automation.AutomationManagementClient(new CertificateCloudCredentials(subscriptionId, cert));
var ct = new CancellationToken();
var content = await client.Runbooks.ListByNameAsync("MyAutomationAccountName", "MyRunbookName", ct);
var firstOrDefault = content?.Runbooks.FirstOrDefault();
if (firstOrDefault != null)
{
var operation = client.Runbooks.Start("MyAutomationAccountName", new RunbookStartParameters(firstOrDefault.Id));
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Also in portal:
1) Application is multitenant
2) Permissions to other applications section - Windows Azure Service Manager - Delegated permissions "Access Azure Service Management(preview)"
Ensure that your Management certificate has private key and was not made from the .CER file. The fact that you're not supplying a password when generating the X509Certificate object makes me think you're using public key only
Ensure that your Managemnet's certificate public key (.CER file) has been uploaded to the Azure management portal (legacy version, Management Certificate area)
Use CertificateCloudCredentials and not any other credential type of an object
Ok, stupid really but one of the tutorials I followed suggested installing the prerelease version of the libs.
Installing the preview (0.15.2-preview) has fixed the issue!

Categories

Resources