I have to implement key rotations in my application. I have some idea how to do that but I am not sure if everything is OK with that solution.
Ok, lets start. I have couple places in my application where I use the KeyVaultClient(Azure KeyVault client) for decrypting purpose. It's work preety well. In my application are some places where KeyVaultClient is used for encrypting. For now(still development phase) I am using hardcoded params(vaultBaseUrl, keyName, keyVersion). But I want to go further and move this params to app.config file.
And here the question begun what to do with keyVersion variable(rest of them I think I can easy store in app.config file, isn't it?) I have couple ideas:
For encrypting:
I could store current keyVersion in app.config, and use this value each time I will encrypt data.
I could read from KeyVaultClients all Keys(GetKeysAsync), next filter them by active flag and order by expiration date. Finally use the newest one.
For decrypting:
I could store keyVersion used for encryption in encrypted data(the encryption result I am converting to Base64String). I mean I could add to string result the 32 characters(keyVersion) prefix.
No more ideas, maybe using the keyVersion from app.config, but it creates problem with keys rotations.
Maybe there is some tool/library that handle this all work for me? :p
For now, new keys are inserted manually, by administrator. In next phases I am going to implement scheduled task for that.
Maybe there is some tool/library that handle this all work for me?
As you mentioned that you need to encrypt key version is consistent with decrypting key version. It is better that if you could share your scenario. Take Encrypt blob for example. If the blob is encrypted,it will have a Metadata["encryptiondata"] with keyId in it. In your case, maybe you also could add a property with keyId for the object. When you try to decrypt then you could get the keyId from the object.
For now, new keys are inserted manually, by administrator. In next phases I am going to implement scheduled task for that.
If you want to create keys, you could do that with following this code sample in WebJob or Azure function.
static string _clientId= "xxxxxxxxxxxxx";
static string _clientSecret = "xxxxxxxxxxxxxxxx";
static string _tenantId = "xxxxxxxxxxxxxxxx";
public static async Task<string> GetAccessToken(string azureTenantId, string azureAppId, string azureSecretKey)
{
var context = new AuthenticationContext("https://login.windows.net/" + _tenantId);
ClientCredential clientCredential = new ClientCredential(_clientId, _clientSecret);
var tokenResponse = await context.AcquireTokenAsync("https://vault.azure.net", clientCredential);
var accessToken = tokenResponse.AccessToken;
return accessToken;
}
var kv = new KeyVaultClient(GetAccessToken);
var result = kv.CreateKeyAsync(vault,keyName,keyType).Result;
Related
How do I retrieve the "secret value" in my Azure Function without using the properties of the Azure function?
Note: The value for the secret URL can be different every time the function is called, so it needs to resolve while running the application and not using configuration or function properties.
Similar problem but not solved using IBinder (my preferred solution):
Can Azure Key Vault be used with Functions to store the connection string for queue triggers?
How to map Azure Functions secrets from Key Vault automatically
[FunctionName("functionName")]
public async static void Run(
[QueueTrigger("queueName", Connection = "StorageConnectionAppSetting")],
IBinder binder,
ILogger log)
{
// TODO: how to resolve access from IBinder
binder.
}
background for the problem:
I have an application that has stored key/value pair within the Azure KeyVault in secrets
while storing the data, I keep the Identifier for later retrieval:
"ClientSecretUri":"https://keyvault.vault.azure.net:443/secrets/1-ff6b03fc-12e8-427f-fa18-08d845672373/78c0211ceb5140a8990dec450eef1d23"
my code for storing the value is:
var kvc = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(
AzureAccessToken.GetAccessToken(APP_CLIENT_ID, APP_CLIENT_SECRET)
));
var secretBundle = await kvc.SetSecretAsync(KEYVAULT_BASE_URI, keyToStoreInValut, clientSecret);
var ClientSecretUri = secretBundle.SecretIdentifier.Identifier;
As the code you provided, you can get the secret Identifier with ClientSecretUri.
Then you could use ClientSecretUri to access the latest secret value.
I have a nice Azure Active Directory set up with a dozen users. (All me!) So I have a Tenant ID, client ID and Client Secret.
I am also working on a simple console application that will function as a public client for this directory. This client also holds a list of usernames and passwords as this is just meant as a simple experiment. Not secure, I know. But I first need to understand how it works...
I do this:
IConfidentialClientApplication client = ConfidentialClientApplicationBuilder
.CreateWithApplicationOptions(options).Build();
And this creates my client app. Works fine.
I also get a token using "https://graph.microsoft.com/.default" and can use this to get all users as JSON:
string result = await GetHttpContentWithToken("https://graph.microsoft.com/v1.0/users",
token.AccessToken);
Although I might want it to be more user-friendly, JSON is fine for now.
How can I check if user is an authorized user?
And no, I don't want complex solutions that require various nuget packages. Just a plain and simple step-by-step explanation. I could probably Google this but I ended up with thousands of results and none were helpful... This should be easy, right?
[EDIT] I first wanted to get a list of users nut that failed because of a typo... (There's a dot before 'default'...)
It took some fooling around but it's not too difficult after all. There are a lot of libraries around Azure but it is all basically just a bunch of HTTP requests and responses. Even in a console application...
I started with making a PublicClientApplicationBuilder first:
var options = new PublicClientApplicationOptions()
{
ClientId = <**clientid**>,
TenantId = <**tenantid**>,
AzureCloudInstance = AzureCloudInstance.AzurePublic,
};
var client = PublicClientApplicationBuilder.CreateWithApplicationOptions(options).Build();
I can also create a ConfidentialClientApplication instead, but this allows me to log in interactively, if need be.
Next, set up the scopes:
var scopes = new List<string>() { "https://graph.microsoft.com/.default" };
As I wanted to log in using username and password, I have to use this:
var token = await client.AcquireTokenInteractive(scopes).ExecuteAsync();
But if I want to log in using code, I can also use this:
var password = new SecureString();
foreach (var c in <**password**>) { password.AppendChar(c); }
var token = await client.AcquireTokenByUsernamePassword(scopes, <**account**>, password).ExecuteAsync();
At this point, I'm authorized as the specified user. So, now all I need is to get whatever data I like, in JSON strings...
public static async Task<string> ExecCmd(string name, string url, string token)
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
string result = await GetHttpContentWithToken(url, token);
JObject json = JsonConvert.DeserializeObject(result) as JObject;
File.WriteAllText(name, json.ToString());
return result;
}
As I just want to read the data as text files, I just execute the action in using a specific and write it as formatted JSON to the file . So, using this simple method I can now use this:
await ExecCmd("Profile.txt", "https://graph.microsoft.com/v1.0/me/", token.AccessToken);
await ExecCmd("Groups.txt", "https://graph.microsoft.com/v1.0/groups", token.AccessToken);
await ExecCmd("Users.txt", "https://graph.microsoft.com/v1.0/users", token.AccessToken);
These will provide me with (1) the profile of the current user, (2) the AD groups and (3) the AD users. And probably a bit more...
I can use this ExecCmd to retrieve a lot more data, if I want to. But there's something else to keep in mind! For it all to work, you also need to configure the Azure application and make sure all access rights are assigned and approved!
So, in Azure AD you have to add an "App registration" and fiddle around with the settings... (The Azure experts are horribly shocked now, but when you want to learn, you'd just have to try and fail until you succeed...)
Also set "Default client type" to "public client" for the registered app.
In Azure, with the registered app, you also need to set the proper API permissions! Otherwise, you won't have access. And as I want access to Active Directory, I need to add permissions to "Azure Active Directory Graph". I can do this inside Azure or by using the scope when I call AcquireTokenInteractive(). For example, by using "https://graph.windows.net/Directory.Read.All" instead of "https://graph.windows.net/.default".
Once you've accessed a token interactively, you can also get more tokens using client.AcquireTokenSilent(). It gets a bit tricky from here, especially if you want to access a lot of different items. Fortunately, Active Directory is mostly the directory itself, groups, users and members.
Personally, I prefer to grant access from the Azure website but this is quite interesting.
Anyways, I wanted to authenticate users with Azure and now I know how to do this. It still leaves a lot more questions but this all basically answers my question...
I'll use this as answer, as others might find it useful...
Currently I have a hard-coded secret key I use for my JWT Token Generation. What is the best way to generate this randomly when generating the token? Also, what I don't understand is if the secret is randomly generated, how can it be that the secret would be randomly generated again for authentication purposes. Am I missing something here or am I way off on how this works? It appears that the secret key is not even random. Is it something I would store in web.config for example
Just expanding on #nodd13's post to I have used the following (in LinqPad) to randomly generate a key:
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = Convert.ToBase64String(key);
// make safe for url
var urlEncoded = base64Secret.TrimEnd('=').Replace('+', '-').Replace('/', '_');
urlEncoded.Dump();
This is indeed random and as I understand it you only need to do this once and you can then store this in your web.config to be referenced later.
I used the following code from this blog post
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = TextEncodings.Base64Url.Encode(key);
I need to store "password like information" in a database field. I would like it to be encrypted but I need to decrypt it before using it. So I can not use a Hash/Salt solution.
Granted if an attacker made it that far into the database it may be too far gone but I figure this would at least stop the mistaken dump of the data.
How to encrypt a value store it into the database and decrypt the same value for use later?
Hashing is not an option (I use it on other parts actually).
Where to store the private key? Users would not supply anything.
This a C# solution so .NET specific stuff would be great. My question is very similar but I am looking for a .net based solution: Two-way encryption: I need to store passwords that can be retrieved
EDIT:
Hogan pretty much answered my question. I found examples out there and they ranged from very complicated to rather simple. It looks like AES is still good so I will be using that method. thank you for all your help.
One solution that does not involve private keys is using DPAPI.
You can use it from .NET via the ProtectedData class.
Here is an example:
public void Test()
{
var password = "somepassword";
var encrypted_password = EncryptPassword(password);
var decrypted_password = DecryptPassword(encrypted_password);
}
public string EncryptPassword(string password)
{
var data = Encoding.UTF8.GetBytes(password);
var encrypted_data = ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encrypted_data);
}
public string DecryptPassword(string encrypted_password)
{
var encrypted_data = Convert.FromBase64String(encrypted_password);
var data = ProtectedData.Unprotect(encrypted_data, null, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(data);
}
Please note that DPAPI in this case depends on the current logged in user account. If you encrypt the password when your application is running as User1, then you can only decrypt the password running under the same user account. Please note that if you change the windows password for User1 in an incorrect way, then you will lose the ability to decrypt the password. See this question for details.
If you don't want use DPAPI, and prefer to have a private key. Then the best place to store such private key is in the user's key store. However, in order to store a private key in the local user store, you need to have a certificate for it. You can create a self signed certificate and store it with its corresponding private key into the local user certificate store.
You can access the user store in code using the X509Store class. You can use it to find the certificate (which is in C# a X509Certificate2 class) that you want to use and then use it to do encryption/decryption.
See this and this for more details.
I've got some sensitive information that I want to be encrypted and stored in Azure Table Storage. Honestly, from a naive approach, using the same AES key for all values probably would be sufficient as I would nowhere near approach having enough data encrypted in order for someone to do any meaningful cryptanalysis. But, I know that the best practice is to limit usage of the same symmetric key.
Recently, Microsoft released client side encryption for Azure Table Storage via Azure KeyVault. It allows you to generate an RSA key and store it in KeyVault and the client library will generate a new symmetric key for every row in table storage and it encrypts the symmetric key with your RSA key. This is perfect because there is no way to do differential cryptanalysis on the ciphertext since they all used different keys. It is especially nice because their library does all the plumbing, all you have to do is grab your RSA key from KeyVault, decorate your designated properties with the EncryptPropertyAttribute and it handles everything else.
Therein lies the rub... I personally find KeyVault kind of obtuse to use and manage. You have to use powershell to set up oauth authentication between your app and keyvault and it looks like a tremendous amount of overhead for storing a single RSA key. If we have hundreds of keys to store, I can imagine it would be much more useful.
Is there any way to use all of Microsoft's client side encryption code without storing the RsaKey in the KeyVault?
It took me a while to find it, but yes, you can store your RSA key outside of KeyVault. You just need to use the RsaKey constructor overload that takes in an RSACryptoServiceProvider that you grab from wherever you deem prudent. I grab mine out of my web.config. However, I make sure that my production RsaCsp is not store in source control and I add it directly in the Azure Web App configuration screen.
IKey tableStorageKey = GetTableStorageKey()
_tableRequestOptions = new TableRequestOptions
{
EncryptionPolicy = new TableEncryptionPolicy(tableStorageKey, null)
};
...
private IKey GetTableStorageKey()
{
using (var rsaCsp = new RSACryptoServiceProvider(2048))
{
try
{
//it doesn't really matter where you get your RsaCsp from, I have mine in my webconfig
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
XmlElement node = doc.SelectSingleNode("/configuration/MyTableStorageRsaKey") as XmlElement;
rsaCsp.FromXmlString(node.OuterXml);
return new RsaKey("MyTableStorageRsaKey", rsaCsp);
}
finally
{
rsaCsp.PersistKeyInCsp = false;
}
}
}
In Microsoft.Azure.KeyVault.Cryptography, there is a change in RsaKey constructor.
Now it does not import key from RSACryptoServiceProvider, but uses it directly, and disposes it in Dispose() method. So usage will change to:
public RsaKey GetFromXmlString(string xmlString)
{
try
{
var rsaCsp = new RSACryptoServiceProvider(2048, new CspParameters() { KeyContainerName = "MyTableStorageRsaKey" });
rsaCsp.FromXmlString(xmlString);
rsaCsp.PersistKeyInCsp = false;
return new RsaKey("MyTableStorageRsaKey", rsaCsp);
}
catch (Exception ex)
{
throw new InvalidOperationException("Invalid rsa key xmlString provided", ex);
}
}
Notice that instance of RSACryptoServiceProvider is not disposed.
Also notice that RsaKey is IDisposable.